<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ React Hooks - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ React Hooks - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:45 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/react-hooks/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 React、React Hooks 和 Axios 进行 CRUD 操作 ]]>
                </title>
                <description>
                    <![CDATA[ 如果你正在使用React，理解和实现API请求可能是相当困难的。 所以在这篇文章中，我们将通过使用React、React Hooks、React Router和Axios实现CRUD操作来学习这一切。 让我们深入了解一下。 如何安装Node和npm 首先，让我们在系统中安装Node。我们将主要用它来执行我们的JavaScript代码。 去官方网站下载Node，https://nodejs.org/en/。 你还需要node包管理器 ，如npm，它是内建在Node上的。你可以用它来为你的JavaScript应用程序安装包。幸运的是，Node自带npm，所以你不需要单独下载它。 一旦它们都完成了，打开你的终端或命令提示符，输入node -v。这将检查你有哪个版本的Node。 如何创建你的React应用 为了创建你的React应用，在终端输入 npx create-react-app <你的应用程序名称>， 或者本例中的**npx create-react-app**react-crud**。 你会看到软件包正在被安装。 一旦软件包安装完毕，进入项目文件夹，输入npm st ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-perform-crud-operations-using-react/</link>
                <guid isPermaLink="false">645859f60f634b0716652bb9</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Axios ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Fri, 05 May 2023 11:10:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/05/React-CRUD-Operations-using-React-and-React-Hooks-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-perform-crud-operations-using-react/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Perform CRUD Operations using React, React Hooks, and Axios</a>
      </p><!--kg-card-begin: markdown--><p>如果你正在使用React，理解和实现API请求可能是相当困难的。</p>
<p>所以在这篇文章中，我们将通过使用React、React Hooks、React Router和Axios实现CRUD操作来学习这一切。</p>
<p>让我们深入了解一下。</p>
<h2 id="nodenpm">如何安装Node和npm</h2>
<p>首先，让我们在系统中安装Node。我们将主要用它来执行我们的JavaScript代码。</p>
<p>去官方网站下载Node，<a href="https://nodejs.org/en/">https://nodejs.org/en/</a>。</p>
<p>你还需要<strong>node包管理器</strong>，如npm，它是内建在Node上的。你可以用它来为你的JavaScript应用程序安装包。幸运的是，Node自带npm，所以你不需要单独下载它。</p>
<p>一旦它们都完成了，打开你的终端或命令提示符，输入<code>node -v</code>。这将检查你有哪个版本的Node。</p>
<h2 id="react">如何创建你的React应用</h2>
<p>为了创建你的React应用，在终端输入 <strong><strong><code>npx create-react-app &lt;你的应用程序名称&gt;</code></strong></strong>， 或者本例中的**<code>npx create-react-app**react-crud</code>**。</p>
<p>你会看到软件包正在被安装。</p>
<p>一旦软件包安装完毕，进入项目文件夹，输入<code>npm start</code>。</p>
<p>你会看到默认的React模板，像这样：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-124754.png" alt="默认的 React Boilerplate 模板" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>默认的 React Boilerplate 模板</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-124858.png" alt="我们的 App.js文件" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>我们的 App.js文件</figcaption>
</figure>
<h2 id="reactsemanticui">如何为React安装Semantic UI包（库）</h2>
<p>让我们在我们的项目中安装Semantic UI React软件包。Semantic UI是一个为React制作的UI库，它有预建的UI组件，比如表格、按钮和许多功能。</p>
<p>你可以使用下面的一个命令来安装它，这取决于你的包管理器。</p>
<pre><code class="language-bash">yarn add semantic-ui-react semantic-ui-css
</code></pre>
<p>对于使用 Yarn 包管理器</p>
<pre><code class="language-bash">npm install semantic-ui-react semantic-ui-css
</code></pre>
<p>对于使用 NPM 包管理器</p>
<p>同时，在你的应用程序的主入口文件中导入该库，也就是index.js。</p>
<pre><code class="language-js">import 'semantic-ui-css/semantic.min.css'
</code></pre>
<p>在你的index.js文件中粘贴上面一行内容。</p>
<h2 id="crud"><strong>如何构建你的CRUD应用</strong></h2>
<p>现在，让我们开始使用React构建我们的CRUD应用。</p>
<p>首先，我们要给我们的应用程序添加一个标题。</p>
<p>在我们的app.js文件中，添加一个标题，像这样：</p>
<pre><code>import './App.css';

function App() {
  return (
    &lt;div&gt;
      React Crud Operations
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>在我们的应用程序中添加一个标题</p>
<p>现在，让我们确保它居中。</p>
<p>给父级div一个classname，即main。在App.css文件中，我们将使用Flexbox来使标题居中。</p>
<pre><code>import './App.css';

function App() {
  return (
    &lt;div className="main"&gt;
      React Crud Operations
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>app.js文件，在父 div 中的 className 为main的css定义。</p>
<pre><code>.main{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}
</code></pre>
<p>我们的 app.css 文件</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-130340.png" alt="Screenshot-2021-07-24-130340" width="600" height="400" loading="lazy"></p>
<p>现在我们的标题已经完全居中了。</p>
<p>为了让它看起来更好一些，我们需要使它加粗，并添加一些很酷的字体。要做到这一点，我们将在我们的标题周围使用标题标签，像这样：</p>
<pre><code>import './App.css';

function App() {
  return (
    &lt;div className="main"&gt;
      &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>让我们从 Google Font导入一种字体. 从 <a href="https://fonts.google.com/">https://fonts.google.com/</a>选择一种。</p>
<p>选择任何你喜欢的字体，但我将使用Montserrat字体家族。</p>
<p>在App.css文件中导入你选择的字体，像这样：</p>
<pre><code>@import url('https://fonts.googleapis.com/css2?family=Montserrat&amp;display=swap');
</code></pre>
<p>现在，让我们改变标题的字体。</p>
<pre><code>&lt;div className="main"&gt;
      &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
    &lt;/div&gt;
</code></pre>
<p>给 <code>h2</code>一个 <code>lassName</code> 为 <code>main-header</code>，就像上面。</p>
<p>然后，在你的 App.css文件，添加font family：</p>
<pre><code>.main-header{
  font-family: 'Montserrat', sans-serif;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-132749.png" alt="Screenshot-2021-07-24-132749" width="600" height="400" loading="lazy"></p>
<p>现在你会看到改变后的标题。</p>
<h2 id="crud">如何创建你的CRUD组件</h2>
<p>让我们创建四个CRUD组件，它们将是创建、读取、更新和删除。</p>
<p>在我们的src文件夹中，创建一个名为组件的文件夹。在这个文件夹中，创建三个文件--创建、读取和更新。对于删除，我们不需要任何额外的组件。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133242.png" alt="Screenshot-2021-07-24-133242" width="600" height="400" loading="lazy"></p>
<p>现在，让我们来实现这些。</p>
<p>但为此，我们需要使用Mock API。这些API将向我们将要创建的假服务器发送数据，这只是为了学习的目的。</p>
<p>所以，请前往 <a href="https://mockapi.io/">https://mockapi.io/</a> ，创建账号。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133456.png" alt="MockAPI" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>MockAPI</figcaption>
</figure>
<p>通过点击<code>(plus)加号</code>按钮创建一个项目。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133553.png" alt="Screenshot-2021-07-24-133553" width="600" height="400" loading="lazy"></p>
<p>点击<code>(plus)加号</code>按钮，创建一个新的项目。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133702.png" alt="Screenshot-2021-07-24-133702" width="600" height="400" loading="lazy"></p>
<p>添加你的项目名称，然后点击<code>(create)创建</code>按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133748.png" alt="Screenshot-2021-07-24-133748" width="600" height="400" loading="lazy"></p>
<p>现在，通过点击 <code>(NEW RESOURCE)新资源</code> 按钮创建一个新资源。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133847.png" alt="Screenshot-2021-07-24-133847" width="600" height="400" loading="lazy"></p>
<p>它将要求你提供<code>(RESOURCE name)资源名称</code>，所以输入你想使用的名称。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134009.png" alt="Screenshot-2021-07-24-134009" width="600" height="400" loading="lazy"></p>
<p>删除额外的字段，<code>如姓名(name)、头像(avatar)或创建时间(createdAt）</code>，因为我们将不需要这些。然后，点击<code>创建(create)</code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134140.png" alt="Screenshot-2021-07-24-134140" width="600" height="400" loading="lazy"></p>
<p>现在，我们已经创建了我们的 <code>fake API(假API)</code>，我把它命名为fakeData。</p>
<p>点击fakeData，你会看到API在一个新的标签中打开。现在的数据库是空的。</p>
<h2 id="createcomponent">如何为(create Component)创建组件创建一个表格</h2>
<p>让我们使用Semantic UI库中的一个表单。</p>
<p>前往Semantic React，在左边的搜索栏中搜索Form。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134532.png" alt="Screenshot-2021-07-24-134532" width="600" height="400" loading="lazy"></p>
<p>你会看到一个像这样的表格，点击左上方的 <code>试试(Try it)</code>，就可以得到代码。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134654.png" alt="Screenshot-2021-07-24-134654" width="600" height="400" loading="lazy"></p>
<p>复制这段代码并将其粘贴到你的Create.js文件中，像这样：</p>
<pre><code>import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'

const Create = () =&gt; (
    &lt;Form&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;First Name&lt;/label&gt;
            &lt;input placeholder='First Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;Last Name&lt;/label&gt;
            &lt;input placeholder='Last Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
        &lt;/Form.Field&gt;
        &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
    &lt;/Form&gt;
)

export default Create;
</code></pre>
<p>在你的app.js文件中导入创建组件(Create Component)。</p>
<pre><code>import './App.css';
import Create from './components/create';

function App() {
  return (
    &lt;div className="main"&gt;
      &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
      &lt;div&gt;
        &lt;Create/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>就像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-135249.png" alt="Screenshot-2021-07-24-135249" width="600" height="400" loading="lazy"></p>
<p>但这里有一个问题--项目没有正确对齐，文本输入标签颜色是黑色的。所以，让我们来改变它。</p>
<p>在create.js文件中，给<strong>Form</strong>一个<code>create-form</code>的className。</p>
<pre><code>import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'

const Create = () =&gt; (
    &lt;Form className="create-form"&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;First Name&lt;/label&gt;
            &lt;input placeholder='First Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;Last Name&lt;/label&gt;
            &lt;input placeholder='Last Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
        &lt;/Form.Field&gt;
        &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
    &lt;/Form&gt;
)

export default Create;
</code></pre>
<p>app.js</p>
<p>并在你的App.css文件中添加以下类。</p>
<pre><code>.create-form label{
  color: whitesmoke !important;
  font-family: 'Montserrat', sans-serif;
  font-size: 12px !important;
}
</code></pre>
<p>App.css</p>
<p>这个类将针对所有的表格字段标签并应用白烟的颜色。它还将改变字体并增加字体大小。</p>
<p>现在，在我们的主<code>className</code>中，添加一个flex-direction属性。这个属性将设置方向为列，所以主<code>className</code>中的每个元素都将水平对齐。</p>
<pre><code>.main{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #212121;
  color: whitesmoke;
  flex-direction: column;
}
</code></pre>
<p>App.css</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-140141.png" alt="Screenshot-2021-07-24-140141" width="600" height="400" loading="lazy"></p>
<p>你可以看到，我们的表单现在看起来好多了。</p>
<p>接下来，让我们从表单字段中获取数据到我们的控制台(console)。为此，我们将使用React的<code>useState</code>钩子。</p>
<p>在我们的create.js文件中，从React中导入<code>useState</code>。</p>
<pre><code>import React, { useState } from 'react';
</code></pre>
<p>然后，为名字(first name)、姓氏(last name)和复选框(checkbox)创建状态。我们将这些状态初始化为空或假。</p>
<pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'

export default function Create() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' /&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' /&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>你可以看到，这现在是作为一个函数组件。所以，我们需要把这个组件改成一个函数组件。这是因为我们只能在函数组件中使用钩子。</p>
<p>现在，让我们分别使用<code>setFirstName</code>、<code>setLastName</code>和<code>setCheckbox</code>属性来设置名字、姓氏和复选框。</p>
<pre><code>&lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;

&lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;

&lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
</code></pre>
<p>我们正在获得名字(first name)、姓氏(last name)和复选框(checkout)的状态。</p>
<p>创建一个名为<code>postData</code>的函数，我们将用它来向API发送数据。在该函数中，写下这段代码。</p>
<pre><code>const postData = () =&gt; {
        console.log(firstName);
        console.log(lastName);
        console.log(checkbox);
}
</code></pre>
<p>我们在控制台中打印出名字(firstName)、姓氏(lastName)和复选框(checkbox)的值。</p>
<p>在(Submit button)提交按钮上，使用onClick事件调用这个函数，这样，每当我们按下提交按钮，这个函数就会被调用。</p>
<pre><code>&lt;Button onClick={postData} type='submit'&gt;Submit&lt;/Button&gt;
</code></pre>
<p>这里是 <em>create</em> 文件的全部代码。</p>
<pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'

export default function Create() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);
    const postData = () =&gt; {
        console.log(firstName);
        console.log(lastName);
        console.log(checkbox);
    }
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button onClick={postData} type='submit'&gt;Submit&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>在名字和姓氏中输入一些数值，并勾选复选框。然后，点击提交按钮。你会看到控制台中打印出的数据是这样的。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-142717.png" alt="Screenshot-2021-07-24-142717" width="600" height="400" loading="lazy"></p>
<h2 id="axiosmockapis">如何使用Axios向Mock APIs发送请求</h2>
<p>让我们使用Axios来发送我们的表单数据到模拟服务器。</p>
<p>但首先，我们需要安装它。</p>
<p>只要输入<code>npm i axios</code>来安装这个包。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-174213.png" alt="Screenshot-2021-07-24-174213" width="600" height="400" loading="lazy"></p>
<p>软件包安装完毕后，让我们开始(create)创建操作。</p>
<p>在文件的顶部导入Axios。</p>
<pre><code>import axios from 'axios';
</code></pre>
<p>导入Axios</p>
<p>在<code>postData</code>函数中，我们将使用Axios来发送POST请求。</p>
<pre><code>const postData = () =&gt; {
        axios.post(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`, {
            firstName,
            lastName,
            checkbox
        })
    }
</code></pre>
<p>发送 Post 请求</p>
<p>如你所见，我们正在使用axios.post。在axios.post中, 我们有 API endpoint(接入点 请求地址), 这是我们之前创建的。然后，我们有被大括号包裹的表单字段。</p>
<p>当我们点击提交时，这个函数将被调用，它将向API服务器发布数据。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-174834.png" alt="Screenshot-2021-07-24-174834" width="600" height="400" loading="lazy"></p>
<p>输入你的名字(first name)，姓氏(last name)，并勾选复选框。点击提交。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-174930.png" alt="Screenshot-2021-07-24-174930" width="600" height="400" loading="lazy"></p>
<p>然后，你检查这个API的返回值，你会得到你的名字、姓氏，复选框为真的值，被包裹在一个对象中。</p>
<h2 id="">如何实现读取和更新操作</h2>
<p>为了开始(read)读取操作，我们需要创建一个读取页面。我们还需要React Router包来导航到不同的页面。</p>
<p>前往<a href="https://reactrouter.com/web/guides/quick-start">https://reactrouter.com/web/guides/quick-start</a>查看文档，同时运行 <code>npm i react-router-dom</code>进行安装。</p>
<p>安装完毕后，从React Router导入一些东西：</p>
<pre><code>import { BrowserRouter as Router, Route } from 'react-router-dom'
</code></pre>
<p>从<code>React Router</code>中导入<code>Router</code>和<code>Route</code>。</p>
<p>在我们的App.js中，把整个返回包成一个Router。这基本上意味着，无论这个Router里面有什么，都能在React中使用。</p>
<pre><code>import './App.css';
import Create from './components/create';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Create /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre>
<p>我们的App.js现在看起来会像上面的样子。</p>
<p>替换掉返回里面的Create，并添加以下代码。</p>
<pre><code>import './App.css';
import Create from './components/create';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Route exact path='/create' component={Create} /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre>
<p>在这里，我们使用Route组件作为Create。我们已经将Create的路径设置为'/create'。因此，如果我们进入<a href="http://localhost:3000/create">http://localhost:3000/create</a> ，我们将看到创建页面。</p>
<p>同样地，我们需要(read)读取和(update)更新的路由。</p>
<pre><code>import './App.css';
import Create from './components/create';
import Read from './components/read';
import Update from './components/update';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Route exact path='/create' component={Create} /&gt;
        &lt;/div&gt;
        &lt;div style={{ marginTop: 20 }}&gt;
          &lt;Route exact path='/read' component={Read} /&gt;
        &lt;/div&gt;

        &lt;Route path='/update' component={Update} /&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre>
<p>因此，(read)读取和(update)更新路由，类似你上面看到的。</p>
<p>如果你前往 <a href="http://localhost:3000/read">http://localhost:3000/read</a>，会看到下面的：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-180318.png" alt="Read Route" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Read Route</figcaption>
</figure>
<p>在<a href="http://localhost:3000/update">http://localhost:3000/update</a> 网址，我们可以看到更新组件（Update Component），像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-180440.png" alt="Screenshot-2021-07-24-180440" width="600" height="400" loading="lazy"></p>
<h3 id="">读取操作</h3>
<p>对于读取操作，我们将需要一个表组件。因此，前往React Semantic UI，并使用库中的一个表。</p>
<pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Registration Date&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;E-mail address&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Premium Plan&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;John Lilki&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;September 14, 2013&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jhlilk22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;No&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Jamie Harington&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;January 11, 2014&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jamieharingonton@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Jill Lewis&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;May 11, 2014&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jilsewris22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>Read.js</p>
<p>在这里，你可以看到我们有一个带有一些假数据(dummy data)的表。但我们只需要一个表行。所以，让我们删除其他的。</p>
<pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Registration Date&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;E-mail address&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Premium Plan&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;John Lilki&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;September 14, 2013&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jhlilk22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;No&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>Read.js</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-182905.png" alt="Screenshot-2021-07-24-182905" width="600" height="400" loading="lazy"></p>
<p>这是“阅读”页面的输出。我们有一个有四列的表，但我们只需要三列。</p>
<p>删除多余的字段列，并像这样重新命名字段。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-183105.png" alt="Screenshot-2021-07-24-183105" width="600" height="400" loading="lazy"></p>
<p>这就是我们的阅读页面现在的样子：</p>
<pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;First Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Last Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Checked&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Nishant&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Kumar&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>Read.js</p>
<p>现在，让我们发送GET请求，从API获得数据。</p>
<p>当我们的应用程序加载时，我们需要这些数据。所以，我们要使用<code>useEffect</code>钩子(hook)。</p>
<pre><code>import React, { useEffect } from 'react';

 useEffect(() =&gt; {
       
 }, [])
</code></pre>
<p>useEffect钩子(hook)</p>
<p>创建一个包含传入数据的状态。这将是一个数组。</p>
<pre><code>import React, { useEffect, useState } from 'react';

const [APIData, setAPIData] = useState([]);
useEffect(() =&gt; {
       
}, [])
</code></pre>
<p>APIData state 来存储API传入的数据</p>
<p>在<code>useEffect</code>钩子(hook)中，让我们发送GET请求。</p>
<pre><code> useEffect(() =&gt; {
        axios.get(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`)
            .then((response) =&gt; {
                setAPIData(response.data);
            })
    }, [])
</code></pre>
<p>因此，我们使用axios.get来向API发送GET请求。然后，如果请求被满足，我们就在我们的_APIData_状态中设置响应数据。</p>
<p>现在，让我们根据API数据来映射我们的表行。</p>
<p>我们将使用Map函数来做这件事。它将对数组进行迭代，并在输出中显示数据。</p>
<pre><code>&lt;Table.Body&gt;
  {APIData.map((data) =&gt; {
     return (
       &lt;Table.Row&gt;
          &lt;Table.Cell&gt;{data.firstName}&lt;/Table.Cell&gt;
           &lt;Table.Cell&gt;{data.lastName}&lt;/Table.Cell&gt;
           &lt;Table.Cell&gt;{data.checkbox ? 'Checked' : 'Unchecked'}&lt;/Table.Cell&gt;
        &lt;/Table.Row&gt;
   )})}
&lt;/Table.Body&gt;
</code></pre>
<p>我们根据API中的数据来映射firstName、lastName和checkbox。但我们的复选框有一点不同。我在这里使用了一个三元操作符（'?'）。如果data.checkbox为真，输出将是Checked，否则将是Unchecked。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-184955.png" alt="Read.js 输出" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Read.js 输出</figcaption>
</figure>
<h3 id="update">更新(Update)操作</h3>
<p>再为更新创建一个标题，并在表行中为更新按钮创建一列。使用Semantic UI React的按钮。</p>
<pre><code>&lt;Table.HeaderCell&gt;Update&lt;/Table.HeaderCell&gt;

&lt;Table.Cell&gt; 
  &lt;Button&gt;Update&lt;/Button&gt;
&lt;/Table.Cell&gt;
</code></pre>
<p>创建(Update)更新按钮</p>
<p>现在，当我们点击这个按钮，我们应该被重定向到更新页面。为此，我们需要React Router的链接。</p>
<p>从React Router导入Link。并将更新按钮的表格单元格包装成Link标签。</p>
<pre><code>import { Link } from 'react-router-dom';

&lt;Link to='/update'&gt;
  &lt;Table.Cell&gt; 
     &lt;Button&gt;Update&lt;/Button&gt;
   &lt;/Table.Cell&gt;
&lt;/Link&gt;
</code></pre>
<p>为更新按钮添加链接(Link)</p>
<p>因此，如果我们点击更新按钮，我们将被重定向到更新页面。</p>
<p>为了更新列的数据，我们需要它们各自的ID，这从APIs获得。</p>
<p>创建一个名为 "setData "的函数。将其绑定到更新按钮上。</p>
<pre><code> &lt;Button onClick={() =&gt; setData()}&gt;Update&lt;/Button&gt;
</code></pre>
<p>现在，我们需要将数据作为参数传递给上面的函数。</p>
<pre><code> &lt;Button onClick={() =&gt; setData(data)}&gt;Update&lt;/Button&gt;
</code></pre>
<p>并在上面的的函数中，在控制台中打印这些数据。</p>
<pre><code>const setData = (data) =&gt; {
   console.log(data);
}
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-190515.png" alt="控制台的数据" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>控制台的数据</figcaption>
</figure>
<p>点击表中的更新按钮，并查看控制台。你会得到相应表字段的数据。</p>
<p>让我们把这些数据设置到localStorage中。</p>
<pre><code>const setData = (data) =&gt; {
        let { id, firstName, lastName, checkbox } = data;
        localStorage.setItem('ID', id);
        localStorage.setItem('First Name', firstName);
        localStorage.setItem('Last Name', lastName);
        localStorage.setItem('Checkbox Value', checkbox)
}
</code></pre>
<p>在本地存储中(Local Storage)设置数据</p>
<p>我们正在将我们的数据解构为id、firstName、lastName和checkbox，然后我们将这些数据设置到本地存储(Local Storage)。你可以使用本地存储(Local Storage)来在浏览器中的存储数据。</p>
<p>现在，在更新组件中，我们需要一个表单来进行更新操作。让我们复制(reate component)创建组件中的表单。只要把函数的名称从Create改为Update。</p>
<pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'
import axios from 'axios';

export default function Update() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);

    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>我们的更新页面</p>
<p>在Update组件中创建一个`useEffect'钩子(hook)。我们将用它来获取我们之前存储在本地存储的数据。同时，为ID字段再创建一个状态(state)。</p>
<pre><code>const [id, setID] = useState(null);

useEffect(() =&gt; {
        setID(localStorage.getItem('ID'))
        setFirstName(localStorage.getItem('First Name'));
        setLastName(localStorage.getItem('Last Name'));
        setCheckbox(localStorage.getItem('Checkbox Value'))
}, []);
</code></pre>
<p>根据你的keys(字典，map 数据结构)从本地存储设置相应的数据。我们需要在表格字段中设置这些值。当更新页面加载时，它将自动填入这些字段。</p>
<pre><code>&lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' value={firstName} onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' value={lastName} onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
</code></pre>
<p>设置表格字段的值</p>
<p>现在，如果我们点击阅读页面中的更新按钮，我们将被重定向到更新页面，在那里我们将看到所有根据数据自动填充的表单。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-193521.png" alt="更新页面" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>更新页面</figcaption>
</figure>
<p>现在，让我们创建更新请求来更新数据。</p>
<p>创建一个名为<code>updateAPIData</code>的函数。在这个函数中，我们将使用axios.put来发送一个PUT请求，以更新我们的数据。</p>
<pre><code>const updateAPIData = () =&gt; {
    axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
        firstName,
         lastName,
         checkbox
	})
}
</code></pre>
<p>在这里，你可以看到我们在API端点上附加了一个id字段。</p>
<p>当我们点击表中的字段时，它的ID会被存储到本地存储器(Local Storage)中。而在更新页面，我们正在检索它。然后，我们将该ID存储在_<code>id</code>_状态中。</p>
<p>之后，我们将ID传递给端点。这使我们能够更新我们传递ID的字段。</p>
<p>将<code>updateAPIData</code>函数绑定到更新按钮上。</p>
<pre><code>&lt;Button type='submit' onClick={updateAPIData}&gt;Update&lt;/Button&gt;
</code></pre>
<p>将updateAPIData绑定到更新按钮上</p>
<p>点击读取页面中表格的更新按钮，改变你的姓氏（last name），然后点击更新页面中的更新按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-194627.png" alt="更新字段" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>更新字段</figcaption>
</figure>
<p>回到“阅读”页面，或查看API。你会看到你的姓氏(last name)已被改变。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-194756.png" alt="The Mock API" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>The Mock API</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-194822.png" alt="我们的读取表格" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>我们的读取表格</figcaption>
</figure>
<h3 id="">删除操作</h3>
<p>在读取表(read table)中再添加一个Button，我们将用它来进行删除操作。</p>
<pre><code>&lt;Table.Cell&gt;
   &lt;Button onClick={() =&gt; onDelete(data.id)}&gt;Delete&lt;/Button&gt;
&lt;/Table.Cell&gt;
</code></pre>
<p>读取表中的删除按钮</p>
<p>创建一个名为 "onDelete "的函数，并将此函数绑定到删除按钮上。这个函数将在点击删除按钮时接收一个ID参数。</p>
<pre><code>const onDelete = (id) =&gt; {

}
</code></pre>
<p>The Delete Function</p>
<p>We are going to use axios.delete to delete the respective columns.</p>
<pre><code>const onDelete = (id) =&gt; {
  axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`)
}
</code></pre>
<p>从API中删除字段</p>
<p>点击删除按钮并检查API。你会看到数据已经被删除。</p>
<p>我们需要在表被删除后获得该表的数据。</p>
<p>因此，创建一个函数来获得API数据。</p>
<pre><code>const getData = () =&gt; {
    axios.get(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`)
        .then((getData) =&gt; {
             setAPIData(getData.data);
         })
}
</code></pre>
<p>获取API数据</p>
<p>现在，在<code>onDelete</code>函数中，我们需要在删除一个字段后获得更新的数据。</p>
<pre><code>const onDelete = (id) =&gt; {
        axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`)
     .then(() =&gt; {
        getData();
    })
}
</code></pre>
<p>删除一个字段后获得更新数据</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-201047.png" alt="读取表格" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>读取表格</figcaption>
</figure>
<p>因此，现在如果我们在任何字段上点击Delete，它将删除该字段并自动刷新表格。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-201423.png" alt="删除一个字段后读表" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>删除一个字段后读表</figcaption>
</figure>
<h2 id="crud">让我们对我们的CRUD应用程序做一些改进吧</h2>
<p>因此，当我们在Create页面发布我们的数据时，我们只是在模拟(mock)数据库中获得数据。当我们的数据在创建页面中被创建时，我们需要重定向到读取页面。</p>
<p>从React Router导入`useHistory'。</p>
<pre><code>import { useHistory } from 'react-router';
</code></pre>
<p>从React Router导入useHistory</p>
<p>创建变量<code>history</code>使用 <code>let</code>。</p>
<pre><code>let history = useHistory();
</code></pre>
<p>然后，使用history.push函数，在post API被调用后推送到阅读页面。</p>
<pre><code>const postData = () =&gt; {
        axios.post(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`, {
            firstName,
            lastName,
            checkbox
        }).then(() =&gt; {
            history.push('/read')
        })
    }
</code></pre>
<p>在发布API成功后推送到阅读页面</p>
<p>它将使用<code>useHistory</code>钩子(hook)推送到阅读页面。</p>
<p>对更新页面做同样的处理。</p>
<pre><code>import React, { useState, useEffect } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'
import axios from 'axios';
import { useHistory } from 'react-router';

export default function Update() {
    let history = useHistory();
    const [id, setID] = useState(null);
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);

    useEffect(() =&gt; {
        setID(localStorage.getItem('ID'))
        setFirstName(localStorage.getItem('First Name'));
        setLastName(localStorage.getItem('Last Name'));
        setCheckbox(localStorage.getItem('Checkbox Value'));
    }, []);

    const updateAPIData = () =&gt; {
        axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
            firstName,
            lastName,
            checkbox
        }).then(() =&gt; {
            history.push('/read')
        })
    }
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' value={firstName} onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' value={lastName} onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={() =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit' onClick={updateAPIData}&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre>
<p>Update.js</p>
<p>现在你知道如何使用React和React Hooks进行CRUD操作了吧！</p>
<p>另外，如果你想补充学习，你可以观看我在Youtube上的[React CRUD操作视频]（<a href="https://youtu.be/-ZMP8ZladIQ%EF%BC%89%E3%80%82">https://youtu.be/-ZMP8ZladIQ）。</a></p>
<blockquote>
<p><strong>学习愉快</strong></p>
</blockquote>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Hooks 入门教程——10 分钟学会使用 useState Hook ]]>
                </title>
                <description>
                    <![CDATA[ 大家好🌈我很久没有写关于在React中处理状态的文章了。上一次写这个主题，是四年前在这篇文章 [https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/] 中，它似乎帮助了很多人。 我收到了大量的观看和惊人的反馈，所以非常感谢——你们真的很棒！🎸 从那以后已经过去很久了。hook从v16.8版本（2019年）开始在React中出现，在React中使用state时有很多需要跟上的地方。 你正在学习state并想通过useState  hook成为专业人员吗？ 太好了，你来对地方了！拿上一杯咖啡（或茶），系好安全带，我们出发吧！ 顺便说一下——如果你正在寻找如何使用setState（在类组件中），那么我建议你查看我以前的文章，“如何在10分钟内成为使用React setState()的专家” [https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learn-react-usestate-hook-in-10-minutes/</link>
                <guid isPermaLink="false">638daa8d832e3f0781763b97</guid>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keren Ma ]]>
                </dc:creator>
                <pubDate>Fri, 02 Dec 2022 08:22:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/philipp-katzenberger-jVx8JaO2Ddc-unsplash-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/learn-react-usestate-hook-in-10-minutes/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React Hooks for Beginners – Learn to Use the useState Hook in 10 Minutes</a>
      </p><!--kg-card-begin: markdown--><p>大家好🌈我很久没有写关于在React中处理状态的文章了。上一次写这个主题，是四年前在<a href="https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/">这篇文章</a>中，它似乎帮助了很多人。</p>
<p>我收到了大量的观看和惊人的反馈，所以非常感谢——你们真的很棒！🎸</p>
<p>从那以后已经过去很久了。hook从v16.8版本（2019年）开始在React中出现，在React中使用state时有很多需要跟上的地方。</p>
<p>你正在学习state并想通过<strong>useState</strong> hook成为专业人员吗？</p>
<p>太好了，你来对地方了！拿上一杯咖啡（或茶），系好安全带，我们出发吧！</p>
<p>顺便说一下——如果你正在寻找如何使用setState（在类组件中），那么我建议你查看我以前的文章，<a href="https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/">“如何在10分钟内成为使用React setState()的专家”</a>。</p>
<h2 id="reacthook">什么是React Hook？</h2>
<p>hook（钩子）是一种特殊的函数，它允许你“钩入”各种React特性。假设一个函数返回一个有两个值的数组：</p>
<ul>
<li><strong>第一个值:</strong> 一个带有状态state的变量。</li>
<li><strong>第二个值:</strong> 一个带有处理程序handle（改变当前状态的函数）的变量。</li>
</ul>
<p>就是这样，很简单。🥞</p>
<p>记住，在JavaScript中，<strong>"值是函数，函数是值"</strong>。我是在2017年从 <a href="https://www.youtube.com/c/funfunfunction"><strong>MPJ</strong></a>学到这一点的，MPJ是我最喜欢的开发者和youtuber之一。谢谢MPJ所做的一切！</p>
<p>如果这让你有点困惑，这里有一个例子：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/01.png" alt="值就是函数，函数就是值" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>值就是函数，函数就是值</figcaption>
</figure>
<p>让我们看看这里发生了什么：</p>
<ul>
<li>在<strong>a</strong>中，存储一个数字。我的意思是，将值<strong>1</strong>（这是一个数字）赋给一个名为<strong>a</strong>的变量。</li>
<li>在<strong>b</strong>中，存储计算表达式的结果（值）。</li>
<li>在<strong>c</strong>中存储一个函数。你存储了一个未执行的函数，它被存储为一个值，随时可以执行。</li>
<li>在<strong>d</strong>中，我们将<strong>c</strong>的计算结果赋值。</li>
</ul>
<p>你明白我的意思了吗？是的，<strong>函数就是值，值就是函数</strong>！这就是你现在需要知道的。</p>
<p><strong>useState</strong>，特别地，它允许你向函数组件（声明为函数而不是类的组件）添加React状态。</p>
<p>实际上，状态保存在钩子内部，但是可以从“调用”钩子的组件访问。</p>
<h2 id="reacthooks">React Hooks的规则</h2>
<p>除了hook是JavaScript函数这一事实之外，在使用它们时还需要遵循一些规则：</p>
<h3 id="">只在顶层调用钩子</h3>
<p>不要在循环、条件或嵌套函数中调用钩子。总是在React函数（组件）的顶层使用钩子，在任何早期返回之前。</p>
<p>这背后的原因是，每次组件渲染时，必须以相同的顺序调用钩子。这使得React能够正确地保存多个useState和useEffect调用之间的钩子状态。</p>
<h4 id="react">只有React函数的调用钩子</h4>
<p>这意味着你可以从React函数（组件）或自定义钩子调用钩子，但不能从常规JavaScript函数调用。</p>
<p>这里有一个有用的插件<a href="https://www.npmjs.com/package/eslint-plugin-react-hooks">here</a>，它强制执行钩子的规则。这是一个非常有用的方法，所以一定要尝试一下。</p>
<h2 id="usestatehook">useState hook的结构</h2>
<p>要使用useState钩子，你需要知道一些事情。</p>
<p>💡你可以查看下面的图来更好地理解我将在这里解释的内容。</p>
<p>1.你必须从React库导入它<br>
2.你必须在React组件中调用它</p>
<pre><code class="language-javascript">const [state, setState] = useState(initialValue)
</code></pre>
<p>不确定你是否理解了它的解构，所以对于那些第一眼没看出来的人：</p>
<p>我尝试这样做：</p>
<pre><code class="language-javascript">const array = useState(initialValue)
</code></pre>
<p>然后我可以使用状态，在位置0内，作为数组[0]，用处理函数setState，在位置1内，作为数组[1]。</p>
<p>解构数组的声明性更强，因为我们知道它的第一个和第二个位置值，我们知道它们对应于状态值和用于更改它的处理程序。</p>
<pre><code class="language-javascript">const [first, second] = useState(initialValue)
</code></pre>
<p>是的，我们做到了。但我们可以把任何东西称为first和second。唯一的规则是这些变量对应于<strong>useState</strong>函数（hook）返回的数组的第一个和第二个位置。</p>
<pre><code class="language-javascript">const [state, setState] = useState(initialValue)
const [counter, setCounter] = useState(initialCount)
const [something, setSomething] = useState(initialSomething)
</code></pre>
<p>如果你不熟悉解构赋值语法，可以暂停阅读，了解一下<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">MDN</a> 或<a href="https://www.freecodecamp.org/news/destructuring-patterns-javascript-arrays-and-objects/">阅读本教程</a>。</p>
<p>去吧——我会等的！（Edo 喝了一小口 ☕）</p>
<p>3. 然后可以自由渲染状态，或者调用setState来更新状态值。</p>
<p>这是一个最简单的函数示例：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon.png" alt="useState hook的结构" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>useState hook的结构</figcaption>
</figure>
<h2 id="usestatehook">何时使用 useState Hook</h2>
<p>为了理解何时使用这个钩子hook，我们需要从学习何时需要状态state开始。</p>
<p>乍一看，我们认为当我们需要一个随时间变化的变量时，我们需要保持它in state。但在大多数情况下，事实并非如此。我的意思是，如果你的变量可以从其他数据推导出来，那么你就不需要状态了。</p>
<h3 id="state1">State 案例 1</h3>
<p>主题颜色, 根据时间变暗或变亮的，可以从系统数据中推导出来。</p>
<p>我们可以简单地从JS的date函数中获得时间（日期）。所以我们在这里不需要状态，对吧?这是一个可以用表达式或函数声明的常量const。</p>
<h3 id="state2">State 案例 2</h3>
<p>一个模式开关（显示/隐藏模式）。</p>
<p>模式切换可以为true或false，当用户单击按钮时触发。因此，在这种情况下，我们真的需要状态，因为我们不能推导这类信息-它只取决于“何时和是否”用户触发事件。</p>
<p>要注意这个区别——什么可以派生推导，什么取决于用户。</p>
<p>当需要存储来自用户的输入时，你将需要使用<strong>useState</strong>钩子。</p>
<p>💡作为经验法则，你应该只使用状态state来保存这类信息——需要用户输入数据或触发事件。</p>
<p>另一个常用的例子是表单数据。几乎每个应用程序或网站都需要从用户那里收集信息。要做到这一点，通常（或强制）需要有一个表格。</p>
<p>表单数据必须以状态存储，至少在将其持久化到数据库之前是这样。但它也可以从数据库中检索，并使其再次可编辑。</p>
<p>很好，我们继续。</p>
<h2 id="react">如何在React中使用多个状态变量</h2>
<p>如果我们需要处理多个状态，最好和推荐的第一种方法是分别处理它们，像这样：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon--1-.png" alt="猫狗计数器（处理多个状态变量）" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>猫狗计数器（处理多个状态变量）</figcaption>
</figure>
<p>这样做并没有什么错，尽管它看起来很原始。 当我们继续使用JavaScript基本类型（在本例中是数字）时，这是一种很好的线性方法。</p>
<p>你也可以在一个对象中混合状态：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon--2-.png" alt="carbon--2-" width="600" height="400" loading="lazy"></p>
<p>这个例子变得有点复杂。我们已经初始化了一个对象，而不是一个基本类型。当我们调用setPets时，我们必须意识到我们需要展开现有的pets对象，然后添加更改，否则我们将丢失它。</p>
<p>对于旧的setState API，这不是强制性的 – 它会理解你想要更新state object的key。但现在它不是了，我喜欢它。现在它更具声明性，是JavaScript中的基本概念。</p>
<p>如果你不熟悉spread语法，请查看<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">MDN</a>或<a href="https://www.freecodecamp.org/news/javascript-object-destructuring-spread-operator-rest-parameter/">阅读这个有用的教程</a>。</p>
<h2 id="">异步状态</h2>
<p>注意，改变/突变状态是一个异步操作。</p>
<p>让我们来看一个证据：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon--3-.png" alt="状态是异步的（它是有延迟的批处理和更新）" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>状态是异步的（它是有延迟的批处理和更新）</figcaption>
</figure>
<p>所以，我更新了我们最初的狗的例子。这次我创建了一个<strong>handleDogsCount</strong>函数来向你显示它。</p>
<p>在handleDogsCount内部，我用新值调用<strong>setDogs</strong>。</p>
<p>如果我需要立即为另一个操作使用状态值，会发生什么？</p>
<p>对，状态还没更新。实现即时操作的最佳方法是使用传递给handleDogsCount函数的值，并且——暂时忘记狗的状态值——事先知道该值没有及时更新（这很棘手，但事实就是如此）。</p>
<h2 id="">如何以函数的方式突变状态</h2>
<p>好了，现在我们知道状态不会立即改变。还有一个相关的问题。如果你可以每秒点击More按钮1M次，会发生什么？</p>
<p>可能，在点击1M的最后，计数器将是999_998（或更少），而不是预期的1_000_000。</p>
<p>为了避免这种情况发生，我们可以用函数的方式设置状态。我们将获取前一个状态的值，以便React能够正确地批处理所有请求并线性更新状态。这样我们就不会在中间丢失信息。</p>
<p>要做到这一点，你可以简单地做以下几点：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon--4-.png" alt="以函数的方式改变状态" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>以函数的方式改变状态</figcaption>
</figure>
<p>好的，很酷。现在我们确信React在处理我们的1M请求以改变状态时不会错过任何东西。</p>
<p>我们不是抓取dogs变量来添加或减去1，而是依赖于暴露在useState setState handle内部的previousState（在本例中是setDogs函数）。</p>
<p>注意，对象和数组是通过引用进行比较的，因此复杂的状态应该在其他钩子（如<strong>useEffect</strong>）的依赖项数组中得到适当的控制。我们将在后面的另一篇文章中讨论它！</p>
<p>如果你是JavaScript新手，让我给你一个关于我所说的内容的剧透：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/06/carbon--5-.png" alt="引用类型比较" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>引用类型比较</figcaption>
</figure>
<p>正如你所看到的，<strong>c</strong>并不严格等于<strong>d</strong>。是的，去试试吧！JavaScript比较复杂对象（所有非<a href="https://developer.mozilla.org/en-US/docs/Glossary/Primitive">基本类型</a>）的方法是引用，而不是值。</p>
<p>如果我将它字符串化，就意味着我在比较字符串。因为它们都是基本类型，所以它们严格相等（按值比较）。</p>
<h2 id="">如何将状态初始化为函数</h2>
<p>如果需要使用庞大的计算量来初始化状态，那么最好使用函数而不是值来初始化它。（译者注：<strong>expensiveComputation()</strong> 最小化局部变量作用域，保持方法小而集中。）</p>
<pre><code class="language-javascript">const [ dogs, setDogs] = useState(() =&gt; expensiveComputation())
</code></pre>
<p>这意味着我们在延迟初始化变量。初始值只会在初始渲染时赋值（如果是函数的话）。</p>
<p>在随后的渲染中（由于组件或父组件的状态更改），useState钩子的参数将被忽略，并将检索当前值。</p>
<h2 id="">总结</h2>
<p>你已经了解了什么是钩子、钩子的规则、useState如何工作、它的结构以及如何处理多个状态。</p>
<p>你还了解了一些陷阱（比如处理状态对象，或者该状态是异步的），以及一些改进性能的技巧，比如将状态初始化为函数，以避免不断地计算该计算。</p>
<p>希望你喜欢这篇关于<strong>useState</strong>钩子或简单的“state hook”的文章。</p>
<h2 id="">最后但同样重要的</h2>
<p>我是 <a href="https://eduardovedes.com/">Edo</a>，我是freeCodeCamp的倡导者，喜欢帮助人们转行从事软件工程。</p>
<p>如果你正在转行，或者正在考虑转行，在freeCodeCamp专栏读一点我的<a href="https://www.freecodecamp.org/chinese/news/from-civil-engineer-to-web-developer-with-freecodecamp/">故事</a>，可能会激励你。</p>
<p>你可能也会对<a href="https://www.freecodecamp.org/news/how-to-become-a-junior-software-engineer-in-6-months/">“如何在6个月内成为一名初级软件工程师”</a>感兴趣。</p>
<p>如果你喜欢这篇文章，请在<a href="https://twitter.com/eduardovedes">Twitter</a> 上关注我，联系我，这样我们就可以聊天了！</p>
<p>谢谢大家。🌈</p>
<p>Edo</p>
<h3 id="reacthooks">更多关于 React Hooks 的内容</h3>
<ol>
<li><a href="https://reactjs.org/docs/hooks-state.html">React Documentation</a></li>
</ol>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Hooks 初学者指南 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：Learn React Hooks – A Beginner's Guide [https://www.freecodecamp.org/news/the-beginners-guide-to-react-hooks/]，作者： Victor Ikechukwu [https://www.freecodecamp.org/news/author/victor-ikechukwu/] 函数组件（Functional components）并不总是 React 中声明组件的首选方法。 在 React 16.8 版本推出之前，函数组件被视为二等公民。它们不能处理状态（state）、逻辑（logic）和许多其他的 React 特性，而且我们只用它们来向 UI 渲染非常简单的组件。 React 16.8 版通过引入 React Hooks 解决了这些问题，它让开发者在函数组件（Functional components）中使用这些 React 特性。 在这篇文章中，你将学习：  * 什么是 React Hooks  * 四个常见的 React Hooks，并举例说明如何在你的应用 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-beginners-guide-to-react-hooks/</link>
                <guid isPermaLink="false">622b2757cda7d006365a7606</guid>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Fri, 11 Mar 2022 10:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/03/The-Beginner-s-Guide-to-React-Hooks.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/the-beginners-guide-to-react-hooks/">Learn React Hooks – A Beginner's Guide</a>，作者：<a href="https://www.freecodecamp.org/news/author/victor-ikechukwu/">Victor Ikechukwu</a></p><!--kg-card-begin: markdown--><p>函数组件（Functional components）并不总是 React 中声明组件的首选方法。</p>
<p>在 React 16.8 版本推出之前，函数组件被视为二等公民。它们不能处理状态（state）、逻辑（logic）和许多其他的 React 特性，而且我们只用它们来向 UI 渲染非常简单的组件。</p>
<p>React 16.8 版通过引入 React Hooks 解决了这些问题，它让开发者在函数组件（Functional components）中使用这些 React 特性。</p>
<p>在这篇文章中，你将学习：</p>
<ul>
<li>什么是 React Hooks</li>
<li>四个常见的 React Hooks，并举例说明如何在你的应用程序中编写它们</li>
<li>最后，我们将看看如何编写你自己的自定义 React Hooks</li>
</ul>
<h2 id="reacthooks">什么是 React Hooks</h2>
<p>Hooks 是 React 16.8 版本中引入的内置 React 函数。它们允许你在函数组件（Functional components）中使用 React 库的功能，如生命周期方法（lifecycle methods）、状态（state）和上下文（context ），而不必担心将其重写为一个类（class）。</p>
<p>每个 React Hook 名称的前缀是 <code>"use"</code> 一词。例如，<code>useState</code>或<code>useEffect</code>。选择这种格式是因为 Hooks 让开发者使用 React 库的特殊功能。所以你是在<code>use</code> React 库的那个特殊功能。</p>
<h2 id="reacthooks">为什么使用 React Hooks</h2>
<p>许多开发者对学习 React Hooks 持怀疑态度，但你不应该这样。以下是你应该开始使用 React Hooks 的几个原因：</p>
<h3 id="react">React 中的类可能是相当混乱的</h3>
<p>类是正确学习 React 的一个障碍。为了使用它们，你需要了解<code>this</code>关键字是如何工作的。你还需要不断记住绑定事件处理程序，以及在 React 中使用类时遇到的其他多余的方法。</p>
<h3 id="classescomponents">类组件（Classes components）部分很复杂，可能难以理解</h3>
<p>类组件通常很大，并试图进行许多操作。从长远来看，它们变得难以理解。</p>
<p>Hooks 解决了这个问题，它允许你把大的组件分离成各种小的功能，而不是把所有的逻辑都强加到一个单一的组件中。</p>
<h3 id="hooks">Hooks 的组件更短，可读性更好</h3>
<p>类组件带有大量的模板代码。考虑一下下面的计数器组件：</p>
<pre><code class="language-javascript">class Counter extends Component {
    constructor(props) {
        super(props)
        this.state = {
         count: 1,
        }
    }
    render() {
        return (
            &lt;div&gt;
                The Current Count: {this.state.count}
                &lt;div&gt;
                &lt;button onClick={this.setState({ count: this.state.count - 1 })}&gt;
                add
                &lt;/button&gt;
                &lt;button onClick={this.setState({ count: this.state.count + 1 })}&gt;
                subtract
                &lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
    );
    }
}
</code></pre>
<p>下面是使用函数组件（functional component）和 React Hooks 的等效代码：</p>
<pre><code class="language-javascript">function Counter  ()  {
    const [count, setCount] = useState(1);
    return (
        &lt;div&gt;
            The Current Count: {this.state.count}
            &lt;div&gt;
                &lt;button onClick={() =&gt; setCount(count + 1)}&gt;add&lt;/button&gt;
                &lt;button onClick={() =&gt; setCount(count - 1)}&gt;subtract&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
};
</code></pre>
<p>请注意，类组件要复杂得多。你需要一个类来扩展 React，一个构造函数来初始化状态，而且你需要到处引用<code>this</code> 关键字。</p>
<p>使用函数组件（functional components）可以消除很多这些，所以我们的代码变得更短，更容易阅读和维护。</p>
<h2 id="reacthooks">使用 React Hooks 的规则</h2>
<p>在使用 React Hooks 时，有几个规则需要遵守：</p>
<ul>
<li><strong>只在组件的顶层调用 Hooks</strong>：你不应该在循环、条件或嵌套函数中使用 Hooks。相反，总是在你的 React 函数的顶层使用 Hooks，在任何 <code>return</code> 关键字之前。</li>
<li><strong>只从React函数中调用Hooks</strong>：不要从普通的 JavaScript 函数中调用 Hooks。你可以：<br>
✅ 从 React 函数组件（functional components）中调用 Hooks<br>
✅ 从自定义 Hooks 中调用 Hooks</li>
</ul>
<h2 id="reacthooks">最常见的 React Hooks</h2>
<p>到目前为止，React 有 10 个内置 Hooks。让我们看看四个最常见的 Hooks：</p>
<ul>
<li><code>useState</code></li>
<li><code>useEffect</code></li>
<li><code>useContext</code></li>
<li><code>useReducer</code></li>
</ul>
<h3 id="usestatehook">useState Hook</h3>
<p>useState Hook 允许你在函数组件（functional components）内创建、更新和操作状态。</p>
<p>React 有一个状态的概念，它是持有我们的组件所依赖的数据的变量，并可能随着时间的推移而改变。每当这些变量发生变化时，React 就会用状态变量的当前值重新渲染 DOM 中的组件来更新 UI。</p>
<p>这个钩子需要一个可选的参数，一个状态（state）的初始值。然后它返回一个包含两个值的数组：</p>
<ul>
<li>state 变量</li>
<li>一个用于更新状态的函数</li>
</ul>
<p>让我们以一个计数器组件为例来看看：</p>
<p>要使用一个 Hook，第一步是在文件的顶部导入 Hook：</p>
<pre><code class="language-javascript">import { useState } from "react";
</code></pre>
<p>然后，用一个值初始化 Hook。由于它返回一个数组，你可以使用数组解构来访问数组中的各个项目，就像这样：</p>
<pre><code class="language-javascript">const [count, setCount] = useState(0);
</code></pre>
<p>有了这个，该组件的代码将是：</p>
<pre><code class="language-javascript">import { useState } from "react";

function Counter() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);
    return (
        &lt;div&gt;
        Current Cart Count: {count}
            &lt;div&gt;
            &lt;button onClick={() =&gt; setCount(count - 1)}&gt;Add to cart&lt;/button&gt;
            &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Remove from cart&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>下面是该组件渲染后的样子。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Counter.gif" alt="Counter" width="600" height="400" loading="lazy"></p>
<p>通过点击 <strong>Add to cart</strong> 或 <strong>Remove from cart</strong> 按钮，状态变量计数的值将发生变化，组件将以更新的状态值重新呈现。</p>
<h3 id="useeffecthook">useEffect Hook</h3>
<p>如果你熟悉 React 类的生命周期方法（lifecycle methods），你可以把 <code>useEffect</code>Hook 看作是<code>componentDidMount</code>、<code>componentDidUpdate</code> 和 <code>componentWillUnmount</code> 生命周期方法在一个函数中的结合。它让你在函数组件（functional components）中复制 React 的生命周期方法。</p>
<p><code>useEffect</code> Hook 让你在函数组件（functional components）中执行副作用（side effects）。副作用是可以与组件的主要操作一起运行的动作，如外部 API 交互、修改 state 变量（state variables）和数据获取。</p>
<p><code>useEffect</code> hook 接收两个参数：</p>
<ul>
<li>一个包含要运行的代码的函数</li>
<li>一个包含组件范围（props、context 和 state variables）数值列表的数组，被称为依赖数组，它告诉 Hook 在每次更新其数值时都要运行。如果不提供，Hook 将在每次渲染后运行。</li>
</ul>
<p>下面是一个使用 Hook 的例子：</p>
<pre><code class="language-javascript">import { useState, useEffect } from "react";
function Counter() {
    // Declare state variables
    const [count, setCount] = useState(0);
    const [product, setProduct] = useState("Eggs");
    useEffect(() =&gt; {
     console.log(`${product} will rule the world!`);
    });
    return (
        &lt;div&gt;
        Current {product}'s count: {count}
            &lt;div&gt;
                &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Add to cart&lt;/button&gt;
                &lt;button onClick={() =&gt; setCount(count - 1)}&gt;Remove from cart&lt;/button&gt;
                Change Product:{" "}
                &lt;input type="text" onChange={(e) =&gt; setProduct(e.target.value)} /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>在这个例子中，该效果将在每次状态更新后运行。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Effect-default.gif" alt="Effect-default" width="600" height="400" loading="lazy"></p>
<h4 id="effect">如何有条件地启动一个 Effect</h4>
<p>要想在某些值发生变化时才运行 Hook，可以将变量作为依赖关系传入数组：</p>
<pre><code class="language-javascript">useEffect(() =&gt; {
 console.log(`${product} will rule the world!`);
}, [product]); // Only re-run the effect if the value of product changes
</code></pre>
<p>有了这个变化，Hook 将只在第一次渲染时运行，并且当产品的价格发生变化时。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Effect-dependency-array.gif" alt="Effect-dependency-array" width="600" height="400" loading="lazy"></p>
<h4 id="">如何在第一次渲染时运行一次</h4>
<p>如果你想让一个 Effect 在第一次渲染时只运行一次，比如在组件第一次渲染时进行 API 调用，你可以像这样传递一个空数组作为其依赖。</p>
<pre><code class="language-javascript">useEffect(() =&gt; {
 console.log("This runs once on first render");
}, []);
</code></pre>
<p>通过提供一个空数组，它告诉 Hook 听从零状态变化（zero state changes），所以它只运行一次。</p>
<h3 id="usecontexthook">useContext Hook</h3>
<p><code>useContext</code> Hook 与 React Context API 一起工作。它为你提供了一种方法，使整个应用程序中的所有组件都能访问特定的数据，无论它们的嵌套有多深。</p>
<p>React 有一个单向的数据流，数据只能从父代传递到子代。要把数据（如 state）从父组件传给子组件，你需要根据子组件的嵌套深度，把它作为一个 prop，通过不同的级别向下传递。</p>
<p>对于诸如用户的首选语言、主题或认证用户的属性等数据，必须手动地在组件树上传递它们是很乏味的。</p>
<p>React的Context API和 <code>useContext</code> Hook 使得在应用程序的所有组件中传递数据变得容易。</p>
<p>它接受一个用<code>React.createContext</code>创建的上下文对象，并像这样返回当前的上下文:</p>
<pre><code class="language-javascript">const value = useContext(SomeContext);
</code></pre>
<p>让我们来看看 Hook 是如何工作的一个例子：</p>
<p>首先，创建一个使用 Hook 的上下文（context）。例如，这里有一个 UserContext 来获取当前用户的值：</p>
<pre><code class="language-javascript">import React from "react";
// some mock context values
const users = [
{
    name: "Harry Potter",
    occupation: "Wizard",
},
{
    name: "Kent Clark",
    occupation: "Super hero",
},
];

export const UserContext = React.createContext(users);
</code></pre>
<p>每个上下文（context）都有一个包装器提供者（Provider wrapper），它允许其子组件订阅上下文（context）的变化，并通过一个值 prop 传递上下文的值。</p>
<p>如果提供者的值 prop（value prop）被更新，其子组件将以新的上下文值重新渲染。</p>
<pre><code class="language-javascript">function Users() {
return (
    &lt;UserContext.Provider value={users}&gt;
    &lt;UserProfile /&gt;
    &lt;/UserContext.Provider&gt;
);
}
</code></pre>
<p>在这个例子中，<code>UserProfile</code> 被作为上下文的接收组件（ consuming component）。</p>
<pre><code class="language-javascript">import React, { useContext } from "react";
import { UserContext } from "./App";

export function UserProfile() {
    const users = useContext(UserContext);
    return (
        &lt;div&gt;
            {users.map((user) =&gt; (
            &lt;li&gt;
            I am {user.name} and I am a {user.occupation}!
            &lt;/li&gt;
            ))}
        &lt;/div&gt;
    );
}
</code></pre>
<p>这将显示当前用户的属性：</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2023/12/image.png" class="kg-image" alt="image" srcset="https://chinese.freecodecamp.org/news/content/images/size/w600/2023/12/image.png 600w, https://chinese.freecodecamp.org/news/content/images/2023/12/image.png 977w" sizes="(min-width: 720px) 720px" width="977" height="477" loading="lazy"></figure><!--kg-card-begin: markdown--><h3 id="usereducerhook">useReducer Hook</h3>
<p><code>useReducer</code> Hooks 是 <code>useState</code> Hooks 的一个替代品。不同的是，它允许更复杂的逻辑和涉及多个子值的状态更新。</p>
<p>与<code>useState'类似，</code>useReducer'允许你创建类似状态（state-like）的变量，每当它们发生变化时，都会导致用户界面的更新。</p>
<p>这个 Hook 接受 2 个参数：一个 reducer 函数和一个初始状态（initial state）。</p>
<p><code>useReducer(reducer, initialState);</code></p>
<p>它返回一个由两个值组成的数组，这两个值可以被解构为状态的当前值和一个调用函数。</p>
<pre><code class="language-javascript">const [state, dispatch] = useReducer(reducer, initialState);
</code></pre>
<p>让我们了解一下它的参数和返回值：</p>
<ul>
<li><strong>state</strong>：这是传递给 Hook 的 initialState 的当前值。</li>
<li><strong>reducer</strong>：reducer 是一个接受状态和动作的函数。基于这些参数，它决定了状态的值将如何变化。</li>
<li><strong>dispatch</strong>：调用函数是我们将动作传递给 reducer 函数，它分配动作以用于更新状态。</li>
</ul>
<p>通常情况下，我们通过 switch 语句遍历我们在应用程序中的动作类型，以确定状态值将如何变化。这就是 Hook 更新其状态值的方式。</p>
<pre><code class="language-javascript">function reducer(state, action) {
    switch (action.type) {
        case "CASE_1":
        return {
         updatedState,
        };
        case "CASE_2":
        return {
         updatedState,
        };
        default:
         return state;
    }
}
</code></pre>
<p>dispatch function 通常以下列格式派发一个对象：</p>
<pre><code class="language-javascript">dispatch({ type: "ACTION_TYPE", payload: optionalArguments });
</code></pre>
<p>其中 type 是动作的描述，payload 是你要传递给 reducer 的参数。</p>
<h2 id="hooks">如何创建自定义 Hooks</h2>
<p>自定义 Hook 的概念是通过利用已有的 React Hook，将常用的组件逻辑从 UI 中提取到 JavaScript 函数中。这可以帮助你防止代码重复，并让你在多个组件中使这些逻辑可以重复使用。</p>
<p>让我们看看一个自定义 Hook 的例子，它将从我们传递给它的任何有效的 API URL 返回一个响应。</p>
<pre><code class="language-javascript">//useFetch.js
import { useState, useEffect } from "react";

export function useFetch(url) {
 //values
    const [data, setData] = useState(null);
    const [error, setError] = useState("");
    useEffect(() =&gt; {
        fetch(url)
        .then(res =&gt; {
            if (!res.ok) {
            throw Error("something wrong, çould not connect to resource");
        }
        setData(res.json());
        })
        .then(() =&gt; {
         setError("");
        })
        .catch( error =&gt; {
            console.warn(`sorry an error occurred, due to ${error.message} `);
            setData(null);
            setError(error.message);
        });
    }, [url]);
    return [data, error];
}
</code></pre>
<p>现在，你可以在你的应用程序中的任何地方使用这种逻辑，只需导入该函数并将 API 路径作为参数传递，而不是从头开始编写。</p>
<h2 id="">结语</h2>
<p>我希望你能看到 React Hooks 是多么有用。它们可以让你快速创建有效的组件，而不用担心类组件所带来的麻烦。</p>
<p>从让你专注于写你的主要代码到允许你创建你自己的自定义 Hooks......React Hooks 是如此的酷！我很高兴你能自己尝试它们。</p>
<p>如果你觉得这篇文章有帮助，请与你的朋友分享它。另外，欢迎在 <a href="https://twitter.com/Victor_codejs">Twitter</a> 和我的<a href="https://vickyikechukwu.hashnode.dev/">博客</a>上与我联系，我在那里分享了大量的免费教育文章和资源。</p>
<p>谢谢你阅读本文，并祝你编程愉快！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 使用 React Hooks 创建可复用的动画组件 ]]>
                </title>
                <description>
                    <![CDATA[ 动画总是会取悦用户。看到各种文章的介绍，你可能会觉得开发者们喜欢使用 React Hooks ，但我发现自己开始慢慢对 Hooks 产生厌倦了。 某个意外的发现让我对 React Hooks 有了新的认识，它不仅仅是一种新的开发方式。也许你已经从文章标题猜到是什么了，没错，就是动画！ 我正在开发一个基于 React 的，使用网格布局组合卡片组件的应用，当删除某个卡片组件时，为它添加动画效果，看起来像下面一样： 但是，和图中效果相比较始终还是有点细微差别。在我的接下来的解决方案中，很好地利用了 React Hooks。 我们将要做什么？  * 开始构建一个基本的项目骨架  * 为元素的消失添加动画效果，解决一些小问题  * 最终效果实现后，将其重构为一个可复用的动画组件  * 在顶部导航和侧边导航中使用该动画组件 如果你没耐心，这里有整个项目的仓库地址 [https://github.com/csepulv/animated-visibility] ，每一步都有相应的标记（链接地址和描述参考 README 文件）。 骨架 我使用 create-react-app [https ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/animating-visibility-with-css-an-example-of-react-hooks/</link>
                <guid isPermaLink="false">5dbaefd7ca1efa04e196a2b1</guid>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leo Zou ]]>
                </dc:creator>
                <pubDate>Thu, 31 Oct 2019 14:31:50 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1572366798564-d40537fbe594.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>动画总是会取悦用户。看到各种文章的介绍，你可能会觉得开发者们喜欢使用 React Hooks ，但我发现自己开始慢慢对 Hooks 产生厌倦了。</p><p>某个意外的发现让我对 React Hooks 有了新的认识，它不仅仅是一种新的开发方式。也许你已经从文章标题猜到是什么了，没错，就是动画！</p><p>我正在开发一个基于 React 的，使用网格布局组合卡片组件的应用，当删除某个卡片组件时，为它添加动画效果，看起来像下面一样：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/11/2.gif" class="kg-image" alt="2" width="600" height="654" loading="lazy"></figure><p>但是，和图中效果相比较始终还是有点细微差别。在我的接下来的解决方案中，很好地利用了 React Hooks。</p><h2 id="-">我们将要做什么？</h2><ul><li>开始构建一个基本的项目骨架</li><li>为元素的消失添加动画效果，解决一些小问题</li><li>最终效果实现后，将其重构为一个可复用的动画组件</li><li>在顶部导航和侧边导航中使用该动画组件</li></ul><p>如果你没耐心，这里有整个项目的仓库<a href="https://github.com/csepulv/animated-visibility">地址</a>，每一步都有相应的标记（链接地址和描述参考 README 文件）。</p><h2 id="--1">骨架</h2><p>我使用 <a href="https://facebook.github.io/create-react-app/">create-react-app</a> 创建了一个简单的应用程序，它是一个简单的卡片网格结构，每个单独卡片可以被隐藏。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/11/3.gif" class="kg-image" alt="3" width="600" height="784" loading="lazy"></figure><p>实现代码很简单，效果也很无趣。当用户点击眼睛图标时，我们改变卡片的 <code>display</code> 属性。</p><pre><code>function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);
  function hideMe() {
    setVisible(false);
  }
  let style = { borderColor: color, backgroundColor: color };
  if (!visible) style.display = "none";
  return (
    &lt;div className="box" style={style}&gt;
      {" "}
      &lt;div className="center"&gt;{word}&lt;/div&gt;{" "}
      &lt;button className="button bottom-corner" onClick={hideMe}&gt;
        {" "}
        &lt;i className="center far fa-eye fa-lg" /&gt;{" "}
      &lt;/button&gt;{" "}
    &lt;/div&gt;
  );
}
</code></pre><p>（上面的代码中使用到了 React Hooks，但这不是 Hooks 最有趣的用途）</p><h2 id="--2">添加动画</h2><p>我没有构建自己的动画库，而是使用了一个像 <a href="https://daneden.github.io/animate.css/">animate.css</a> 这样的动画库。<a href="https://github.com/digital-flowers/react-animated-css">react-animated-css</a> 是一个很好的库，它为 animate.css 提供了一个包装器。</p><p>安装 react-animated-css</p><pre><code class="language-shell">npm install --save react-animated-css
</code></pre><p>在 <code>index.html</code> 中添加 animate.css</p><pre><code class="language-html">&lt;link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.css" /&gt;
</code></pre><p>在上面的 <code>Box</code> 组件中，将渲染结果改为</p><pre><code>return (
  &lt;Animated animationIn="zoomIn" animationOut="zoomOut" isVisible={visible}&gt;
    &lt;div className="box" style={style}&gt;
      &lt;div className="center"&gt;{word}&lt;/div&gt;
      &lt;button className="button bottom-corner" onClick={hideMe}&gt;
        &lt;i className="center far fa-eye fa-lg" /&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  &lt;/Animated&gt;
);
</code></pre><h2 id="--3">不完全是我们想要的东西</h2><p>animate.css 会为 <code>opacity</code> 和其他 css 属性添加动画；但不能在 <code>display</code> 属性上添加 css 过渡效果，所以将卡片隐藏后，它始终在文档流中占据着位置。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/11/4.gif" class="kg-image" alt="4" width="600" height="766" loading="lazy"></figure><p>如果你搜索一下，<a href="https://stackoverflow.com/questions/13037637/css-animation-and-display-none">有些解决方案</a>是建议使用定时器在动画结束时设置 <code>display: none</code>。</p><p>所以我们可以添加以下代码。</p><pre><code>function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);
  const [fading, setFading] = useState(false);

  function hideMe() {
    setFading(true);
    setTimeout(() =&gt; setVisible(false), 650);
  }

  let style = { borderColor: color, backgroundColor: color };

  return (
    &lt;Animated
      animationIn="zoomIn"
      animationOut="zoomOut"
      isVisible={!fading}
      style={visible ? null : { display: "none" }}
    &gt;
      &lt;div className="box" style={style}&gt;
        &lt;div className="center"&gt;{word}&lt;/div&gt;
        &lt;button className="button bottom-corner" onClick={hideMe}&gt;
          &lt;i className="center far fa-eye fa-lg" /&gt;
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/Animated&gt;
  );
}
</code></pre><p>(注意：默认的动画时长是 1000ms，我使用的是 650ms，为了在设置 <code>display</code> 属性之前减少卡顿/暂停现象，这只是个人喜好)。</p><p>这样我们就能得到想要的效果。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/11/5.gif" class="kg-image" alt="5" width="600" height="735" loading="lazy"></figure><h2 id="--4">构建一个可复用的组件</h2><p>现在到此为止，但目前有两个问题（对于我来说）</p><ul><li>我不想复制/粘贴 <code>Animated</code> 代码块，样式，功能，来重复实现相同效果。</li><li><code>Box</code> 组件混合了不同类型的逻辑，例如：违反了关注点分离的概念。准确的说，<code>Box</code> 的主要功能是渲染卡片内容，但是动画细节混入了。</li></ul><h2 id="--5">类组件</h2><p>我们可以创建一个传统的 React 类组件来管理和动画相关的状态：切换隐藏/显示，设置 <code>display</code> 属性的超时时间。</p><pre><code>class AnimatedVisibility extends Component {
  constructor(props) {
    super(props);
    this.state = { noDisplay: false, visible: this.props.visible };
  }

  componentWillReceiveProps(nextProps, nextContext) {
    if (!nextProps.visible) {
      this.setState({ visible: false });
      setTimeout(() =&gt; this.setState({ noDisplay: true }), 650);
    }
  }

  render() {
    return (
      &lt;Animated
        animationIn="zoomIn"
        animationOut="zoomOut"
        isVisible={this.state.visible}
        style={this.state.noDisplay ? { display: "none" } : null}
      &gt;
        {this.props.children}
      &lt;/Animated&gt;
    );
  }
}
</code></pre><p>然后使用它</p><pre><code>function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);

  function hideMe() {
    setVisible(false);
  }

  let style = { borderColor: color, backgroundColor: color };

  return (
    &lt;AnimatedVisibility visible={visible}&gt;
      &lt;div className="box" style={style}&gt;
        &lt;div className="center"&gt;{word}&lt;/div&gt;
        &lt;button className="button bottom-corner" onClick={hideMe}&gt;
          &lt;i className="center far fa-eye fa-lg" /&gt;
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/AnimatedVisibility&gt;
  );
}
</code></pre><p>这就实现了一个可复用的组件，但是还有点复杂，我们还可以优化一下。</p><h2 id="react-hooks-and-useeffect">React Hooks and useEffect</h2><p><a href="https://reactjs.org/docs/hooks-intro.html">React Hooks</a> 是 React 16.8 中的新特性，它们为 React 组件的生命周期和状态管理提供了一种更简单的方法</p><p><a href="https://reactjs.org/docs/hooks-effect.html">useEffect</a> 钩子为 <code>componentWillReceiveProps</code> 的使用提供了一种优雅的替代方案,它的代码更简洁，我们还可以使用函数式组件。</p><pre><code>function AnimatedVisibility({ visible, children }) {
  const [noDisplay, setNoDisplay] = useState(!visible);
  useEffect(() =&gt; {
    if (!visible) setTimeout(() =&gt; setNoDisplay(true), 650);
    else setNoDisplay(false);
  }, [visible]);

  const style = noDisplay ? { display: "none" } : null;
  return (
    &lt;Animated
      animationIn="zoomIn"
      animationOut="zoomOut"
      isVisible={visible}
      style={style}
    &gt;
      {children}
    &lt;/Animated&gt;
  );
}
</code></pre><p><code>useEffect</code> 钩子还是有点不一样，它的主要目的是副作用：改变状态，调用异步函数等等。在我们的例子中，它根据之前的 <code>visible</code> 的值修改了内部的 <code>noDisplay</code> 布尔值。</p><p>将 <code>visible</code> 作为依赖添加到 <code>useEffect</code> 的依赖数组中，当 <code>visible</code> 的值发生变化时， <code>useEffect</code> 钩子才会被调用。</p><p>和类组件的杂乱相比较，我认为 <code>useEffect</code> 是一种更好的解决方案。</p><h2 id="-sidebars-navbars">组件复用：Sidebars 和 Navbars</h2><p>大家都喜欢 Sidebar 和 Navbar，我们来添加一个吧。</p><pre><code>function ToggleButton({ label, isOpen, onClick }) {
  const icon = isOpen ? (
    &lt;i className="fas fa-toggle-off fa-lg" /&gt;
  ) : (
    &lt;i className="fas fa-toggle-on fa-lg" /&gt;
  );
  return (
    &lt;button className="toggle" onClick={onClick}&gt;
      {label} {icon}
    &lt;/button&gt;
  );
}

function Navbar({ open }) {
  return (
    &lt;AnimatedVisibility
      visible={open}
      animationIn="slideInDown"
      animationOut="slideOutUp"
      animationInDuration={300}
      animationOutDuration={600}
    &gt;
      &lt;nav className="bar nav"&gt;
        &lt;li&gt;Item 1&lt;/li&gt;
        &lt;li&gt;Item 2&lt;/li&gt;
        &lt;li&gt;Item 3&lt;/li&gt;
      &lt;/nav&gt;
    &lt;/AnimatedVisibility&gt;
  );
}

function Sidebar({ open }) {
  return (
    &lt;AnimatedVisibility
      visible={open}
      animationIn="slideInLeft"
      animationOut="slideOutLeft"
      animationInDuration={500}
      animationOutDuration={600}
      className="on-top"
    &gt;
      &lt;div className="sidebar"&gt;
        &lt;ul&gt;
          &lt;li&gt;Item 1&lt;/li&gt;
          &lt;li&gt;Item 2&lt;/li&gt;
          &lt;li&gt;Item 3&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/div&gt;
    &lt;/AnimatedVisibility&gt;
  );
}

function App() {
  const [navIsOpen, setNavOpen] = useState(false);
  const [sidebarIsOpen, setSidebarOpen] = useState(false);

  function toggleNav() {
    setNavOpen(!navIsOpen);
  }

  function toggleSidebar() {
    setSidebarOpen(!sidebarIsOpen);
  }

  return (
    &lt;Fragment&gt;
      &lt;main className="main"&gt;
        &lt;header className="bar header"&gt;
          &lt;ToggleButton
            label="Sidebar"
            isOpen={sidebarIsOpen}
            onClick={toggleSidebar}
          /&gt;
          &lt;ToggleButton label="Navbar" isOpen={navIsOpen} onClick={toggleNav} /&gt;
        &lt;/header&gt;
        &lt;Navbar open={navIsOpen} /&gt;
        &lt;Boxes /&gt;
      &lt;/main&gt;
      &lt;Sidebar open={sidebarIsOpen} /&gt;
    &lt;/Fragment&gt;
  );
}
</code></pre><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/11/6.gif" class="kg-image" alt="6" width="600" height="545" loading="lazy"></figure><h2 id="--6">还没结束...</h2><p>到这里我们就可以停下了，但就像我之前提到的关注点分离，我更倾向于避免在 <code>Box</code>、<code>Sidebar</code> 和 <code>Navbar</code> 的 render 方法中混合 <code>AnimatedVisibility</code> 组件（代码有点重复）。</p><p>我们可以创建一个高阶组件（HOC）。由于状态管理的原因， HOCs 通常会涉及到类组件。</p><p>但是使用了 React Hooks，我们只需要组合 HOC 就可以了（函数式编程概念）。</p><pre><code class="language-js">function AnimatedVisibility({
  visible,
  children,
  animationOutDuration,
  disappearOffset,
  ...rest
})
// ... same as before
}


function makeAnimated(
  Component,
  animationIn,
  animationOut,
  animationInDuration,
  animationOutDuration,
  disappearOffset
) {
  return function({ open, className, ...props }) {
    return (
      &lt;AnimatedVisibility
        visible={open}
        animationIn={animationIn}
        animationOut={animationOut}
        animationInDuration={animationInDuration}
        animationOutDuration={animationOutDuration}
        disappearOffset={disappearOffset}
        className={className}
      &gt;
        &lt;Component {...props} /&gt;
      &lt;/AnimatedVisibility&gt;
    );
  };
}

export function makeAnimationSlideLeft(Component) {
  return makeAnimated(Component, "slideInLeft", "slideOutLeft", 400, 500, 200);
}

export function makeAnimationSlideUpDown(Component) {
  return makeAnimated(Component, "slideInDown", "slideOutUp", 400, 500, 200);
}

export default AnimatedVisibility
</code></pre><p>然后在 App.js 中使用这些基于函数式的 HOCs</p><pre><code>function Navbar() {
  return (
    &lt;nav className="bar nav"&gt;
      &lt;li&gt;Item 1&lt;/li&gt;
      &lt;li&gt;Item 2&lt;/li&gt;
      &lt;li&gt;Item 3&lt;/li&gt;
    &lt;/nav&gt;
  );
}

function Sidebar() {
  return (
    &lt;div className="sidebar"&gt;
      &lt;ul&gt;
        &lt;li&gt;Item 1&lt;/li&gt;
        &lt;li&gt;Item 2&lt;/li&gt;
        &lt;li&gt;Item 3&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}

const AnimatedSidebar = makeAnimationSlideLeft(Sidebar);
const AnimatedNavbar = makeAnimationSlideUpDown(Navbar);

function App() {
  const [navIsOpen, setNavOpen] = useState(false);
  const [sidebarIsOpen, setSidebarOpen] = useState(false);

  function toggleNav() {
    setNavOpen(!navIsOpen);
  }

  function toggleSidebar() {
    setSidebarOpen(!sidebarIsOpen);
  }

  return (
    &lt;Fragment&gt;
      &lt;main className="main"&gt;
        &lt;header className="bar header"&gt;
          &lt;ToggleButton
            label="Sidebar"
            isOpen={sidebarIsOpen}
            onClick={toggleSidebar}
          /&gt;
          &lt;ToggleButton label="Navbar" isOpen={navIsOpen} onClick={toggleNav} /&gt;
        &lt;/header&gt;
          &lt;AnimatedNavbar open={navIsOpen} /&gt;
        &lt;Boxes /&gt;
      &lt;/main&gt;
      &lt;AnimatedSidebar open={sidebarIsOpen} className="on-top"/&gt;
    &lt;/Fragment&gt;
  );
}
</code></pre><h2 id="--7">接下来呢？</h2><p>对于简单的动画，可以使用我所提到的方法。如果比较复杂，我会使用像 <a href="https://github.com/chenglou/react-motion">react-motion</a> 这样的库。</p><p>不仅仅是动画，React Hooks 让我们可以编写可读性高、更简洁的代码。但是，我们需要在思维上有个调整，像 &nbsp;useEffect 这样的 Hooks 不完全是 React 生命周期函数的替代品，你需要深入学习和研究。</p><p>我建议看看像 <a href="https://usehooks.com/">useHooks.com</a> 这样的网站，还有像 <a href="https://github.com/streamich/react-use">react-use</a> 这样的库（不同钩子用例的集合）。</p><p>原文：<a href="https://www.freecodecamp.org/news/animating-visibility-with-css-an-example-of-react-hooks/">https://www.freecodecamp.org/news/animating-visibility-with-css-an-example-of-react-hooks/</a>，作者：Christian Sepulveda</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
