<?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[ Keren Ma - 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[ Keren Ma - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 31 May 2026 09:18:46 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/dake0913/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 React 和 Dall-E 2 API 生成图像——React 和 OpenAI API 教程 ]]>
                </title>
                <description>
                    <![CDATA[ 嘿，大家好！OpenAI 刚刚发布了它的 DALL-E API，用户可以通过键入查询生成自定义图像。 在本教程中，你将学习如何集成 OpenAI DALL-E 2 API 与 React app。 但首先，Dell-E 是如何工作的呢 正如你已经知道的，你必须输入一个查询——就像 熊拿着画笔在星空里，文森特·梵高画。这里面有很多关键词，比如“画笔”、“星空”和“文森特·梵高”。 Dall-E 要做的是搜索这些与我上面提到的关键字相关的图像。然后它将使用人工智能将所有的图像合并为一个，然后提供给我们。 现在让我们学习如何将其集成到 React 应用程序中，以创建具有这些很棒的特性的应用程序。 如何创建 React 应用程序 现在，创建一个 React 应用程序。你可以使用 CRA（create-react-app）命令创建它，也可以使用 Vite。 我们需要一个文本字段和一个按钮作为 UI 组件。文本字段将用于从用户获取查询，而按钮将用于触发 API 请求。让我们同时创建一个状态来存储查询和一个函数，该函数将在单击按钮时运行。 import { useState } fro ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/generate-images-using-react-and-dall-e-api-react-and-openai-api-tutorial/</link>
                <guid isPermaLink="false">6391629a0bd1810766a86b0b</guid>
                
                    <category>
                        <![CDATA[ 人工智能 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keren Ma ]]>
                </dc:creator>
                <pubDate>Thu, 08 Dec 2022 04:10:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/Important-Concepts-and-questions--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/generate-images-using-react-and-dall-e-api-react-and-openai-api-tutorial/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Generate Images using React and the Dall-E 2 API – React and OpenAI API Tutorial</a>
      </p><!--kg-card-begin: markdown--><p>嘿，大家好！OpenAI 刚刚发布了它的 DALL-E API，用户可以通过键入查询生成自定义图像。</p>
<p>在本教程中，你将学习如何集成 OpenAI DALL-E 2 API 与 React app。</p>
<h2 id="delle">但首先，Dell-E 是如何工作的呢</h2>
<p>正如你已经知道的，你必须输入一个查询——就像 <strong>熊拿着画笔在星空里，文森特·梵高画</strong>。这里面有很多关键词，比如“画笔”、“星空”和“文森特·梵高”。</p>
<p>Dall-E 要做的是搜索这些与我上面提到的关键字相关的图像。然后它将使用人工智能将所有的图像合并为一个，然后提供给我们。</p>
<p>现在让我们学习如何将其集成到 React 应用程序中，以创建具有这些很棒的特性的应用程序。</p>
<h2 id="react">如何创建 React 应用程序</h2>
<p>现在，创建一个 React 应用程序。你可以使用 CRA（create-react-app）命令创建它，也可以使用 Vite。</p>
<p>我们需要一个文本字段和一个按钮作为 UI 组件。文本字段将用于从用户获取查询，而按钮将用于触发 API 请求。让我们同时创建一个状态来存储查询和一个函数，该函数将在单击按钮时运行。</p>
<pre><code>import { useState } from "react";
import "./App.css";

function App() {
  const [prompt, setPrompt] = useState("");

  const generateImage = async () =&gt; {};

  return (
    &lt;div className="app-main"&gt;
      &lt;&gt;
        &lt;h2&gt;Generate an Image using Open AI API&lt;/h2&gt;

        &lt;textarea
          className="app-input"
          placeholder="Search Bears with Paint Brushes the Starry Night, painted by Vincent Van Gogh.."
          onChange={(e) =&gt; setPrompt(e.target.value)}
          rows="10"
          cols="40"
        /&gt;
        &lt;button onClick={generateImage}&gt;Generate an Image&lt;/button&gt;
      &lt;/&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>我们的输出将会像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-212826.png" alt="Screenshot-2022-11-05-212826" width="600" height="400" loading="lazy"></p>
<h3 id="dalle2apireact">如何将 DALL-E 2 API 集成到 React 应用程序</h3>
<p>让我们看看如何将 DALL-E 2 API 集成到我们的应用程序中。</p>
<p>首先，我们需要访问 <a href="https://beta.openai.com">OpenAI</a>网站。你需要注册以生成一个 API 密钥。你的账户里还会有 18 美元可以使用。</p>
<p>在注册时选择创建应用程序。</p>
<p>因此，在你创建了你的账户之后，转到 View API Keys 部分，在那里你可以创建你唯一的 API 密钥。查看下面的图片作为参考。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-213523.png" alt="Screenshot-2022-11-05-213523" width="600" height="400" loading="lazy"></p>
<p>现在在 React App 中，创建一个 <strong>.env</strong> 文件。这是为了存储 API 密钥。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-213733.png" alt="Screenshot-2022-11-05-213733" width="600" height="400" loading="lazy"></p>
<p>在这里添加 API 密钥。请注意，在 CRA 和 Vite React App 中，从 .env 文件中获取密钥的方法是不同的。所以请记住这一点。我使用的是 Vite，所以我们是这样做的：</p>
<pre><code>VITE_Open_AI_Key = "Your API Key"
</code></pre>
<p>现在已经添加了 API 密钥，我们需要在 App.js 或 App.jsx 文件中导入一些东西，包括来自 openai SDK 的 Configuration 和 OpenAIApi。但是，我们首先需要在 React App 安装 <strong>openai SDK</strong>。</p>
<p>只需输入下面的指令安装：</p>
<pre><code>npm install openai
</code></pre>
<p>安装可能需要一些时间。然后，像这样导入我们之前提到的两个东西：</p>
<pre><code>import { Configuration, OpenAIApi } from "openai";
</code></pre>
<p>我们需要创建一个配置变量，它将从 .env 文件中获取 API 密钥。</p>
<pre><code>const configuration = new Configuration({
	apiKey: import.meta.env.VITE_Open_AI_Key,
});
</code></pre>
<p>现在，我们需要将这个配置实例传递给 OpenAIApi，并为 OpenAIApi 创建一个新实例。</p>
<pre><code>const openai = new OpenAIApi(configuration);
</code></pre>
<p>以下是到目前为止的全部代码：</p>
<pre><code>import { Configuration, OpenAIApi } from "openai";

import { useState } from "react";
import "./App.css";

function App() {
  const [prompt, setPrompt] = useState("");
  const configuration = new Configuration({
    apiKey: import.meta.env.VITE_Open_AI_Key,
  });

  const openai = new OpenAIApi(configuration);
  

  return (
    &lt;div className="app-main"&gt;
      &lt;&gt;
        &lt;h2&gt;Generate an Image using Open AI API&lt;/h2&gt;

        &lt;textarea
          className="app-input"
          placeholder="Search Bears with Paint Brushes the Starry Night, painted by Vincent Van Gogh.."
          onChange={(e) =&gt; setPrompt(e.target.value)}
          rows="10"
          cols="40"
        /&gt;
        &lt;button onClick={generateImage}&gt;Generate an Image&lt;/button&gt;
      &lt;/&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>现在，在 <strong>generateImage</strong> 函数中，我们需要调用之前创建的 OpenAIApi 实例。记住，功能需求是异步的。</p>
<pre><code>const generateImage = async () =&gt; {
    await openai.createImage({
      prompt: prompt,
      n: 1,
      size: "512x512",
    });
  };
</code></pre>
<p>如你所见，我们使用的是 <strong>openai.createImage</strong>。这个 API 用于使用用户查询创建图像。它还需要<strong>数量 n</strong>，这是我们希望 API 返回的图像的数量，以及<strong>图像的尺寸 size</strong>。</p>
<p>有三种不同的图像尺寸，不同的价格，如下表所示。如果你使用的是 1024x1024 大小，每张图像将花费 0.020 美元。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-215314.png" alt="Screenshot-2022-11-05-215314" width="600" height="400" loading="lazy"></p>
<p>现在这个 <strong>openai.createImage</strong> 返回一些可以存储在变量中的 response 接口。然后，我们可以从 response 变量获得生成的图像链接。</p>
<pre><code>const generateImage = async () =&gt; {
    const res = await openai.createImage({
      prompt: prompt,
      n: 1,
      size: "512x512",
    });

    console.log(res.data.data[0].url);
  };
</code></pre>
<p>但我们还是别这么做了。让我们再创建一个状态来存储这个图像链接，这样我们就可以在 UI 本身中查看图像。</p>
<pre><code>const [result, setResult] = useState("");

const generateImage = async () =&gt; {
    const res = await openai.createImage({
      prompt: prompt,
      n: 1,
      size: "512x512",
    });

    setResult(res.data.data[0].url);
  };
</code></pre>
<p>现在，图像链接将存储在 <strong>result</strong> 状态中。让我们在 UI 中渲染图像。但是由于结果最初是空的，我们可以创建一个查验。我们将只看到在状态中有链接的图像标记。</p>
<pre><code>{result.length &gt; 0 ? (
          &lt;img className="result-image" src={result} alt="result" /&gt;
        ) : (
          &lt;&gt;&lt;/&gt;
        )}
</code></pre>
<p>And here's the styling too:</p>
<pre><code>.result-image {
  margin-top: 20px;
  width: 350px;
}
</code></pre>
<p>UI 现在看起来像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-220108.png" alt="Screenshot-2022-11-05-220108" width="600" height="400" loading="lazy"></p>
<p>让我们输入一些东西并查看输出：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-220222.png" alt="Screenshot-2022-11-05-220222" width="600" height="400" loading="lazy"></p>
<p>在上面的例子中，我输入了<strong>一匹在红色沙滩里的马</strong>，这是结果。</p>
<p>让我们尝试一些更复杂的东西，比如<strong>熊拿着画笔在星空里，文森特·梵高画</strong></p>
<p>结果如下：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot-2022-11-05-220423.png" alt="Screenshot-2022-11-05-220423" width="600" height="400" loading="lazy"></p>
<p>以上就是你如何创建这个应用。你可以键入任何输入查询，它将通过人工智能生成该图像。</p>
<h2 id="">总结</h2>
<p>这就是所有的内容。现在你知道了如何使用 DALL-E 2 API 创建自己的 React 应用程序，以使用 AI 生成图像。你可以添加更多的功能，所以不妨尝试一下。</p>
<p>如果你想看视频版本，请点击我的视频<a href="https://youtu.be/oacBV4tnuYQ">使用 React 和 Dall-E API 生成图像——React 和 OpenAI API 教程</a> 在我的 YouTube 频道 <a href="https://www.youtube.com/c/CybernaticoByNishant">Cybernatico</a>。</p>
<p>查看代码 <a href="https://github.com/nishant-666/Dall-E-API-with-React">GitHub</a> 以供参考。</p>
<blockquote>
<p>学习快乐~</p>
</blockquote>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 通过代码示例详解 React State ]]>
                </title>
                <description>
                    <![CDATA[ state是React中最复杂的东西，初学者和有经验的开发人员都很难理解。因此，在本文中，我们将探索React中state的所有基础知识。 在理解state之前，让我们先了解一些基础知识，以便稍后更容易理解它。 如何用React在用户界面渲染数据 在屏幕上渲染任何东西，我们要使用ReactDOM.render  方法。 它的语法如下： ReactDOM.render(element, container[, callback])  * element  元素可以是任何HTML元素,JSX或返回JSX的组件  * container  容器是UI上我们想要在其中呈现数据的元素  * callback  回调是可选的函数，我们可以传递它，它在屏幕上渲染或重新渲染时被调用    看看下面的代码： import React from "react"; import ReactDOM from "react-dom"; const rootElement = document.getElementById("root"); ReactDOM.render(<h1>Welcome ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-state-in-react-explained-with-examples/</link>
                <guid isPermaLink="false">638db3ad832e3f0781763c0c</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keren Ma ]]>
                </dc:creator>
                <pubDate>Mon, 05 Dec 2022 04:29:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/state.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/what-is-state-in-react-explained-with-examples/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How State Works in React – Explained with Code Examples</a>
      </p><!--kg-card-begin: markdown--><p>state是React中最复杂的东西，初学者和有经验的开发人员都很难理解。因此，在本文中，我们将探索React中state的所有基础知识。</p>
<p>在理解state之前，让我们先了解一些基础知识，以便稍后更容易理解它。</p>
<h2 id="react">如何用React在用户界面渲染数据</h2>
<p>在屏幕上渲染任何东西，我们要使用<code>ReactDOM.render</code> 方法。</p>
<p>它的语法如下：</p>
<pre><code class="language-plain">ReactDOM.render(element, container[, callback])
</code></pre>
<ul>
<li><code>element</code> 元素可以是任何HTML元素,JSX或返回JSX的组件</li>
<li><code>container</code> 容器是UI上我们想要在其中呈现数据的元素</li>
<li><code>callback</code> 回调是可选的函数，我们可以传递它，它在屏幕上渲染或重新渲染时被调用<br>
看看下面的代码：</li>
</ul>
<pre><code class="language-plain">import React from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

ReactDOM.render(&lt;h1&gt;Welcome to React!&lt;/h1&gt;, rootElement);
</code></pre>
<p>这是一个<a href="https://codesandbox.io/s/focused-shockley-oh4tn?file=/src/index.js">代码示例</a>。</p>
<p>在这里，我们只是向屏幕渲染一个h1元素。</p>
<p>要渲染多个元素，可以这么写：</p>
<pre><code class="language-plain">import React from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

ReactDOM.render(
  &lt;div&gt;
    &lt;h1&gt;Welcome to React!&lt;/h1&gt;
    &lt;p&gt;React is awesome.&lt;/p&gt;
  &lt;/div&gt;,
  rootElement
);
</code></pre>
<p>这是一个<a href="https://codesandbox.io/s/white-hooks-dgru0?file=/src/index.js">代码示例</a>。</p>
<p>如果内容变多，我们可以将JSX取出，放在一个变量中，这是渲染内容的首选方式，就像这样：</p>
<pre><code class="language-plain">import React from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

const content = (
  &lt;div&gt;
    &lt;h1&gt;Welcome to React!&lt;/h1&gt;
    &lt;p&gt;React is awesome.&lt;/p&gt;
  &lt;/div&gt;
);

ReactDOM.render(content, rootElement);
</code></pre>
<p>这是一个<a href="https://codesandbox.io/s/trusting-night-5g825?file=/src/index.js">代码示例</a>。</p>
<p>在这里，我们还添加了一对额外的圆括号，以正确对齐JSX并使其成为单个JSX表达式。</p>
<p>如果你想详细了解JSX及其各种重要特性，请<a href="https://www.freecodecamp.org/news/jsx-in-react-introduction/">在这里查看我的文章</a>。</p>
<p>现在，让我们在屏幕上显示一个按钮和一些文本：</p>
<pre><code class="language-plain">import React from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

let counter = 0;

const handleClick = () =&gt; {
  counter++;
  console.log("counter", counter);
};

const content = (
  &lt;div&gt;
    &lt;button onClick={handleClick}&gt;Increment counter&lt;/button&gt;
    &lt;div&gt;Counter value is {counter}&lt;/div&gt;
  &lt;/div&gt;
);

ReactDOM.render(content, rootElement);
</code></pre>
<p>这是一个<a href="https://codesandbox.io/s/quizzical-cohen-x55p8?file=/src/index.js">代码示例</a>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_initial.gif" alt="counter_initial" width="600" height="400" loading="lazy"></p>
<p>正如你所看到的，当我们单击按钮时，计数器<code>counter</code> 的值会增加，就像您在控制台中看到的那样。但在UI上它没有更新。</p>
<p>这是因为我们在加载页面时，只使用 <code>ReactDOM.render</code>  方法一次，来渲染<code>counter</code> JSX的内容。我们不会再次调用它，所以即使<code>counter</code>的值在更新，它也不会显示在UI上。让我们来解决这个问题。</p>
<pre><code class="language-jsx">import React from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

let counter = 0;

const handleClick = () =&gt; {
  counter++;
  console.log("counter", counter);
  renderContent();
};

const renderContent = () =&gt; {
  const content = (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;Increment counter&lt;/button&gt;
      &lt;div&gt;Counter value is {counter}&lt;/div&gt;
    &lt;/div&gt;
  );

  ReactDOM.render(content, rootElement);
};

renderContent();
</code></pre>
<p>这是一个<a href="https://codesandbox.io/s/adoring-noether-8gsgu?file=/src/index.js">代码示例</a>。<br>
现在，我们在<code>renderContent</code> 函数内封装<code>content</code> JSX 和 <code>ReactDOM.render</code>方法。一旦它被定义好了，我们就调用函数在加载渲染用户界面上的内容。</p>
<p>注意，我们也在<code>renderContent</code> 函数内调用封装了<code>handleClick</code> 函数，所以每次我们点击按钮，<code>renderContent</code> 函数将被调用，界面内容也会更新。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_updated.gif" alt="counter_updated" width="600" height="400" loading="lazy"></p>
<p>正如你所看到的，它正按照预期工作，计数器<code>counter</code> 的值正正确地显示在UI上。</p>
<p>你可能认为在每次单击按钮时重新呈现整个DOM的成本很高——但事实并非如此。这是因为React使用了Virtual DOM算法来检查UI上发生了什么变化，并且只重新呈现发生了变化的元素。因此，整个DOM不会再次被重新渲染。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_preview.gif" alt="counter_preview" width="600" height="400" loading="lazy"></p>
<p>这是一个你可以自己尝试代码的<a href="https://8gsgu.csb.app/">效果预览</a>。</p>
<p>正如您在HTML结构中看到的，只有计数器 <code>counter</code>  的值被重新渲染，因为它是HTML结构中唯一显示的东西。这就是React如此之快的原因，虚拟DOM使React更加有用。</p>
<p>但是，每次我们想要更新UI时调用<code>renderContent</code> 函数仍然是不可行的。所以React添加了“state”的概念。</p>
<h2 id="reactstate">介绍React中的State</h2>
<p>状态允许我们管理应用程序中不断变化的数据。它被定义为一个对象，我们在其中定义键-值对，指定我们希望在应用程序中追踪的各种数据。</p>
<p>在React里，所有的代码在一个component组件里被定义。</p>
<p>在React中创建组件的方法主要有两种：</p>
<ul>
<li>类组件</li>
<li>函数组件</li>
</ul>
<blockquote>
<p>现在我们将从类组件开始。在本文后面，我们将看到一种创建函数组件的方法。<br>
你应该知道如何使用类组件以及函数组件，包括钩子。</p>
</blockquote>
<p>你不应该通过React钩子直接学习函数组件，而是应该首先理解类组件，这样才容易理解基础知识。</p>
<p>你可以通过使用ES6类关键字和扩展React提供的<code>Component</code>类来创建一个组件，如下所示：</p>
<pre><code class="language-jsx">import React from "react";
import ReactDOM from "react-dom";

class Counter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      counter: 0
    };

    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.state.counter = this.state.counter + 1;

    console.log("counter", this.state.counter);
  }

  render() {
    const { counter } = this.state;

    return (
      &lt;div&gt;
        &lt;button onClick={this.handleClick}&gt;Increment counter&lt;/button&gt;
        &lt;div&gt;Counter value is {counter}&lt;/div&gt;
      &lt;/div&gt;
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(&lt;Counter /&gt;, rootElement);
</code></pre>
<blockquote>
<p>注意组件的名称首字母要大写（<code>Counter</code>）。<br>
这是一个 <a href="https://codesandbox.io/s/nostalgic-burnell-57fhd?file=/src/index.js">代码示例</a>。</p>
</blockquote>
<p>看看这里我们在做什么。</p>
<ul>
<li>在构造函数内，首先通过传递props来调用 <code>super</code> 。</li>
<li>然后我们将状态定义为对象，将<code>counter</code> 定义为对象的属性。</li>
<li>我们还将<code>this</code>的环境绑定到<code>handleClick</code>函数因此在<code>handleClick</code>函数中我们得到了<code>this</code>的正确环境。</li>
<li>然后在<code>handleClick</code>函数中，我们更新<code>countor</code>并将其记录到控制台。</li>
<li>在<code>render</code>方法中，我们返回我们想要在UI上渲染的JSX。</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_mutate_state.gif" alt="counter_mutate_state" width="600" height="400" loading="lazy"></p>
<p>正如您在控制台中所看到的，<code>counter</code>正在正确地更新——但它没有在UI上更新。</p>
<p>这是因为我们直接将 <code>handleClick</code>  函数中的状态更新为：</p>
<pre><code class="language-js">this.state.counter = this.state.counter + 1
</code></pre>
<p>因此React不会重新渲染组件（<strong>直接更新状态也是一个不好的做法</strong>）。</p>
<blockquote>
<p>永远不要在React中直接更新/改变状态，因为这是一个糟糕的做法，它会导致应用程序出现问题。另外，如果你直接更改状态，那么在状态更改时不会重新渲染组件。</p>
</blockquote>
<h2 id="setstate">setState的语法</h2>
<p>为了改变状态，React为我们提供了一个setState函数，允许我们更新状态的值。</p>
<p><code>setState</code>函数的语法如下：</p>
<pre><code>setState(updater, [callback])
</code></pre>
<ul>
<li><code>updater</code> 被更新的可以是函数或对象。</li>
<li><code>callback</code>回调函数是一个可选函数，在状态成功更新后执行。</li>
</ul>
<blockquote>
<p>调用 <code>setState</code> 会自动重新渲染整个组件及其所有子组件。我们不需要像前面使用<code>renderContent</code> 函数那样手动重新渲染。</p>
</blockquote>
<h2 id="react">如何使用函数更新React中的状态</h2>
<p>让我们修改 <a href="https://codesandbox.io/s/nostalgic-burnell-57fhd?file=/src/index.js">上面的代码示例</a> 来使用 <code>setState</code> 函数更新状态。</p>
<p>这是更新的 <a href="https://codesandbox.io/s/withered-dust-p3emg?file=/src/index.js">代码示例</a>。</p>
<p>如果你检查更新后的 <code>handleClick</code> 函数，它看起来像这样：</p>
<pre><code class="language-js">handleClick() {
  this.setState((prevState) =&gt; {
    return {
      counter: prevState.counter + 1
    };
  });

  console.log("counter", this.state.counter);
}
</code></pre>
<p>在这里，我们将一个函数作为 <code>setState</code> 方法的第一个参数传递，并返回一个新的状态对象，其中<code>counter</code> 在 <code>counter</code>的上一个值的基础上增加1。</p>
<p>我们在上面的代码中使用箭头函数，但是使用普通函数也可以。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_updated_async.gif" alt="counter_updated_async" width="600" height="400" loading="lazy"></p>
<p>如果您注意到，我们正在正确地获得UI上 <code>counter</code> 的更新值。 但在控制台中,我们获取的是以前的 <code>counter</code> 值，尽管我们在调用 <code>this.setState</code> 之后添加了console.log。</p>
<blockquote>
<p>这是因为 <code>setState</code> 方法本质上是异步的。</p>
</blockquote>
<p>这意味着，尽管我们调用了<code>setState</code> 来一个个地增加 <code>counter</code> ，但它没有立刻生效。这是因为我们调用 <code>setState</code> 方法时，整个组件被重新渲染 – 因此React需要使用虚拟DOM算法检查需要更改的内容，然后执行各种检查以高效更新UI。</p>
<p>这是你可能没办法在调用 <code>setState</code>后，马上获得 <code>counter</code> 更新的原因。</p>
<blockquote>
<p>这在React中是非常重要的，因为你会在写代码时遇到调试困难的问题。请记住<code>setState</code> 在React里是异步的。</p>
</blockquote>
<p>如果您想在使用<code>setState</code> 后立即获得更新后的状态值， 你可以传递一个函数作为第二个参数，状态一更新就调用 <code>setState</code> 。</p>
<p>这是一个调整后的<a href="https://codesandbox.io/s/jolly-dawn-65wis?file=/src/index.js">代码示例</a>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/counter_updated_sync.gif" alt="counter_updated_sync" width="600" height="400" loading="lazy"></p>
<p>正如你所看到的，我们在控制台中和UI上同时获得了正确的 <code>counter</code> 值。</p>
<p>在上面的演示中，<code>handleClick</code>方法是这样的：</p>
<pre><code class="language-js">handleClick() {
  this.setState(
    (prevState) =&gt; {
      return {
        counter: prevState.counter + 1
      };
    },
    () =&gt; console.log("counter", this.state.counter)
  );
}
</code></pre>
<p>这里对于 <code>setState</code> 函数调用，我们传递了两个参数。第一个是返回新状态的函数，第二个是在状态更新后调用的回调函数。我们只是将更新的计数器值记录到回调函数中的控制台。</p>
<blockquote>
<p>尽管React提供了一个回调函数来立即获取更新后的状态值，但建议您只在快速测试或记录日志时使用它。</p>
</blockquote>
<p>相反，React建议你使用<code>componentDidUpdate</code>方法，这是一个React生命周期方法，看起来像这样：</p>
<pre><code class="language-js">componentDidUpdate(prevProps, prevState) {
  if (prevState.counter !== this.state.counter) {
    // do something
    console.log("counter", this.state.counter);
  }
}
</code></pre>
<p>这是一个 <a href="https://codesandbox.io/s/youthful-pine-txb1o?file=/src/index.js">代码示例</a>。</p>
<p>你可以在<a href="https://stackoverflow.com/questions/56501409/what-is-the-advantage-of-using-componentdidupdate-over-the-setstate-callback#answer-56502614">这里</a>找到更多关于为什么使用 <code>componentDidUpdate</code> 而不是回调 <code>setState</code>。</p>
<h2 id="">如何简化状态和方法声明</h2>
<p>如果你在上面的代码示例中看到构造函数代码，你会看到它看起来像这样：</p>
<pre><code class="language-js">constructor(props) {
  super(props);

  this.state = {
    counter: 0
  };

  this.handleClick = this.handleClick.bind(this);
}
</code></pre>
<p>要在 <code>handleClick</code> 事件处理程序中使用 <code>this</code> 关键字，我们必须像这样在构造函数中绑定它： <code>this:handleClick</code></p>
<pre><code class="language-js">this.handleClick = this.handleClick.bind(this);
</code></pre>
<p>此外，要声明状态，我们必须创建一个构造函数，在其中添加一个 <code>super</code> 调用，然后才能声明状态。<br>
这不仅繁琐，而且使代码变得不必要地复杂。</p>
<p>随着事件处理程序数量的增加，<code>.bind</code> 调用的数量也会增加。我们可以使用类属性语法来避免这样做。</p>
<p>这是一个更新的 <a href="https://codesandbox.io/s/sad-bassi-7fxnl?file=/src/index.js">代码示例</a> ，包含类属性语法。</p>
<p>在这里，我们像这样将状态直接移动到类内部：</p>
<pre><code class="language-js">state = {
   counter: 0
};
</code></pre>
<p>并且，<code>handlerClick</code> 事件处理器更改为箭头函数语法如下：</p>
<pre><code class="language-js">handleClick = () =&gt; {
  this.setState((prevState) =&gt; {
    return {
      counter: prevState.counter + 1
    };
  });
};
</code></pre>
<p>由于箭头函数没有自己的 <code>this</code> 上下文，所以它将使用上下文作为类，因此不需要使用 <code>.bind</code>  方法。</p>
<p>这使得代码更简单，更容易理解，因为我们不需要不停地绑定每个事件处理器。</p>
<blockquote>
<p><a href="https://github.com/facebook/create-react-app">create-react-app</a> 已经内置了对它的支持，现在就可以开始使用这个语法了。</p>
</blockquote>
<p>从现在开始，我们将使用这种语法，因为它是编写React组件的更流行和首选的方法。</p>
<p>如果您想了解更多关于类属性语法的知识，请查看 <a href="https://javascript.plainenglish.io/how-to-write-clean-and-easy-to-understand-react-code-using-class-properties-syntax-5b375b0618d3?source=friends_link&amp;sk=c170992cab9025fddb7b34b8894ea993">我的文章</a>。</p>
<h2 id="es">如何使用ES简化语法</h2>
<p>如果你在上面的代码示例中查看 <code>setState</code> 方法的调用，它会像这样：</p>
<pre><code class="language-js">this.setState((prevState) =&gt; {
  return {
    counter: prevState.counter + 1
  };
});
</code></pre>
<p>这么多代码，只是为了从一个函数中返回一个对象，我们用了5行代码。</p>
<p>我们能如下简化成一行：</p>
<pre><code class="language-js">this.setState((prevState) =&gt; ({ counter: prevState.counter + 1 }));
</code></pre>
<p>这里，我们将对象包装在圆括号中，使其隐式返回。这是可行的，因为如果我们在箭头函数中只有一条语句，我们可以跳过return关键字和花括号，像这样：</p>
<pre><code class="language-js">const add = (a, b) =&gt; { 
 return a + b;
}

// the above code is the same as below code:

const add = (a, b) =&gt; a + b;
</code></pre>
<p>但是由于左花括号被认为是函数体的开始，我们需要将对象包装在圆括号内，以使其正常工作。</p>
<p>这是一个更新的<a href="https://codesandbox.io/s/zen-galois-pew17?file=/src/index.js">代码示例</a>。</p>
<h2 id="react">如何在React中使用对象作为状态更新器</h2>
<p>在上面的代码中，我们使用一个函数作为<code>setState</code> 的第一个参数，但我们也可以传递一个对象作为参数。</p>
<p>这是一个<a href="https://codesandbox.io/s/zealous-nobel-yvvmw?file=/src/index.js">代码示例</a>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/updated_name.gif" alt="updated_name" width="600" height="400" loading="lazy"></p>
<p>组件代码如下所示：</p>
<pre><code class="language-js">class User extends React.Component {
  state = {
    name: "Mike"
  };

  handleChange = (event) =&gt; {
    const value = event.target.value;
    this.setState({ name: value });
  };

  render() {
    const { name } = this.state;

    return (
      &lt;div&gt;
        &lt;input
          type="text"
          onChange={this.handleChange}
          placeholder="Enter your name"
          value={name}
        /&gt;
        &lt;div&gt;Hello, {name}&lt;/div&gt;
      &lt;/div&gt;
    );
  }
}
</code></pre>
<p>在这里，我们添加了一个输入文本框，用户在其中输入自己的名字，当用户在文本框中输入时，它会显示在文本框的下方。</p>
<p>在状态中，我们初始化了name属性为 <code>Mike</code>  ，并在输入文本框中添加了一个 <code>onChange</code>  处理程序，如下所示：</p>
<pre><code class="language-js">state = {
  name: "Mike"
};

...

&lt;input
  type="text"
  onChange={this.handleChange}
  placeholder="Enter your name"
  value={name}
/&gt;
</code></pre>
<p>因此，当我们在文本框中键入任何内容时，我们通过向 <code>setState</code>  函数传递一个对象来更新输入值的状态。</p>
<pre><code class="language-js">handleChange = (event) =&gt; {
  const value = event.target.value;
  this.setState({ name: value });
}
</code></pre>
<blockquote>
<p>但是我们应该使用哪种形式的<code>setState</code>呢?我们必须决定是将一个对象还是一个函数作为第一个参数传递给<code>setState</code>函数。</p>
</blockquote>
<p>**答案是：**如果不需要 <code>prevState</code>  参数来查找下一个状态值，则传递一个对象。否则，将该函数作为第一个参数传递给 <code>setState</code>  。</p>
<p>但在传递对象作为参数时需要注意一个问题。</p>
<p>看看这个 <a href="https://codesandbox.io/s/eloquent-panini-u2ooe?file=/src/index.js">代码示例</a>.</p>
<p>在下面的代码中， <code>handleClick</code> 方法看起来像这样：</p>
<pre><code class="language-js">handleClick = () =&gt; {
  const { counter } = this.state;
  this.setState({
    counter: counter + 1
  });
}
</code></pre>
<p>我们取 <code>counter</code>  的当前值然后加1。它运行良好，如下图所示：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_correct.gif" alt="object_setstate_correct" width="600" height="400" loading="lazy"></p>
<p>现在看看这个修改版本的 <a href="https://codesandbox.io/s/busy-johnson-oqvfn?file=/src/index.js">代码示例</a> 。</p>
<p>我们的 <code>handleClick</code> 方法现在看起来像这样：</p>
<pre><code class="language-js">handleClick = () =&gt; {
  this.setState({
    counter: 5
  });

  const { counter } = this.state;

  this.setState({
    counter: counter + 1
  });
}
</code></pre>
<p>在这里，我们首先将 <code>counter</code>  值设置为5，然后将其增加1。所以 <code>counter</code>  的预期值是6。我们来看看是不是这样。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_wrong.gif" alt="object_setstate_wrong" width="600" height="400" loading="lazy"></p>
<p>正如您所看到的，当我们第一次单击按钮时，我们期望 <code>counter</code>  值变成5，但它变成了1，并且在随后的每一次单击它都增加1。</p>
<p>这是因为，正如我们前面所看到的， <code>setState</code>  方法本质上是异步的。当我们调用 <code>setState</code>  时， <code>counter</code>   的值不会立即变为5，因此在下一行我们得到的 <code>counter</code>   值为0，这是我们在开始时初始化的状态。</p>
<p>所以当我们再次调用 <code>setState</code>  将 <code>counter</code>   加1时，它变成1，并且它只继续加1。</p>
<p>要解决这个问题，我们需要使用 <code>setState</code>  的更新程序语法，其中传递一个函数作为第一个参数。</p>
<p>这是一个<a href="https://codesandbox.io/s/strange-silence-qhykz?file=/src/index.js">代码示例</a>。</p>
<p>在上面的示例中， <code>handleClick</code>  方法看起来是这样的：</p>
<pre><code class="language-js">handleClick = () =&gt; {
  this.setState({
    counter: 5
  });

  this.setState((prevState) =&gt; {
    return {
      counter: prevState.counter + 1
    };
  });

  this.setState((prevState) =&gt; {
    return {
      counter: prevState.counter + 1
    };
  });
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_updater.gif" alt="object_setstate_updater" width="600" height="400" loading="lazy"></p>
<p>如您所见，当我们第一次单击按钮时， <code>counter</code>  的值变为7。这和预期的一样，因为首先我们把它设为5，然后把它加2次1，所以它就变成了7。即使我们多次点击按钮，它仍然保持在7，因为每次点击我们都将它重置为5并增加两次。</p>
<p>这是因为在 <code>handleClick</code>  内部，我们调用 <code>setState</code>  将计数器值设置为5，方法是将一个对象作为 <code>setState</code>  函数的第一个参数传递给该函数。在那之后，我们调用了两个 <code>setState</code>  调用，其中我们使用函数作为第一个参数。</p>
<p>那么这是如何正确工作的呢?</p>
<p>当React遇到 <code>setState</code>  调用时，它将调度更新以更改状态，因为它是异步的。但是在完成状态更改之前，React会看到有另一个 <code>setState</code> 调用。</p>
<p>因此，React不会立即使用新的 <code>counter</code>  值重新呈现。相反，它合并所有的 <code>setState</code>  调用，并基于前面 <code>counter</code> 的值更新 <code>counter</code> ，因为我们已经使用了 <code>prevState``.counter</code> 去计算 <code>counter</code>。</p>
<p>一旦所有 <code>setState</code>  调用都成功完成，React才重新渲染组件。因此，即使有三个 <code>setState</code>  调用，React也只会重新渲染组件一次，这可以通过在 <code>render</code>  方法中添加console.log语句来确认。</p>
<blockquote>
<p>因此，需要记住的一点是，在使用对象作为 <code>setState</code>  调用的第一个参数时应该小心，因为它可能会导致不可预知的结果。使用函数作为第一个参数，可以根据前面的结果获得正确的结果。</p>
</blockquote>
<p>你可能不会像我们在上面的演示中所做的那样一遍又一遍地调用 <code>setState</code>，但你可以在另一个函数中调用它，如下所示：</p>
<pre><code class="language-js">state = {
 isLoggedIn: false
};

...

doSomethingElse = () =&gt; {
 const { isLoggedIn } = this.state;
 if(isLoggedIn) {
   // do something different 
 }
};

handleClick = () =&gt; {
  // some code
  this.setState({ isLoggedIn: true);
  doSomethingElse();
}
</code></pre>
<p>在上面的代码中，我们已经定义了一个 <code>isLoggedIn</code>  状态，并且我们有两个函数 <code>handleClick</code>  和 <code>doSomethingElse</code> 。在 <code>handleClick</code>  函数中，我们将 <code>isLoggedIn</code>  状态值更新为<code>true</code>，并立即在下一行调用 <code>doSomethingElse</code>  函数。</p>
<p>在 <code>doSomethingElse</code>  中你可能认为你会得到 <code>isLoggedIn</code>  状态为<code>true if</code>条件中的代码会被执行。但是它不会被执行，因为 <code>setState</code>  是异步的，状态可能不会立即更新。</p>
<p>这就是为什么React添加了 <code>componendDidUpdate</code>  这样的生命周期方法，以便在状态或props更新时做一些事情。</p>
<blockquote>
<p>请注意检查您是否在下一行或下一个函数中再次使用相同的 <code>state</code>  变量，做些事来避免这些结果。</p>
</blockquote>
<h2 id="reactsetstate">如何合并React中的setState调用</h2>
<p>看看 <a href="https://codesandbox.io/s/bold-cache-zcj4u?file=/src/index.js">这个代码示例</a>。</p>
<p>这里，我们在状态中声明了 <code>username</code> 和 <code>counter</code> 属性，如下所示：</p>
<pre><code class="language-js">state = {
  counter: 0,
  username: ""
};
</code></pre>
<p>还如下声明了 <code>handleOnClick</code> 和 <code>handleOnChange</code> 事件处理器：</p>
<pre><code class="language-js">handleOnClick = () =&gt; {
  this.setState((prevState) =&gt; ({
    counter: prevState.counter + 1
  }));
};

handleOnChange = (event) =&gt; {
  this.setState({
    username: event.target.value
  });
};
</code></pre>
<p>检查下面函数中 <code>setState</code> 的调用。 你能看到在 <code>handleOnClick</code> 方法里，我们只设置了<code>counter</code>  的状态，在 <code>handleOnChange</code> 方法里只设置了<code>username</code> 的状态。</p>
<p>所以我们不需要像这样同时设置两个状态变量的状态：</p>
<pre><code class="language-js">this.setState((prevState) =&gt; ({
    counter: prevState.counter + 1,
    username: "somevalue"
}));
</code></pre>
<p>我们只能更新我们想要更新的那个。React会自动合并其他状态属性，所以我们不需要自己手动合并它们。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/state_merged-1.gif" alt="state_merged-1" width="600" height="400" loading="lazy"></p>
<p>正如您所看到的，我们成功地分别独立地更改了 <code>counter</code> 和 <code>username</code>。</p>
<h2 id="react">如何在React的函数组件中使用状态</h2>
<p>到目前为止，我们已经了解了如何在类组件中使用状态。现在让我们看看如何在函数组件中使用它。</p>
<p>函数组件与类组件类似，只是它们没有状态和生命周期方法。这就是为什么您可能听说过它们被称为无状态函数组件。</p>
<p>这些组件只接受props并返回一些JSX。</p>
<p>函数组件使代码更短，更容易理解和测试。</p>
<p>它们的执行速度也快一些，因为它们没有生命周期方法。在类组件中扩展的 <code>React.Component</code>  类 也没有为React带来的额外数据。</p>
<p>看看这个<a href="https://codesandbox.io/s/sleepy-pascal-8ugh3?file=/src/index.js">代码示例</a>。</p>
<p>在这里，我们从<a href="https://randomuser.me/">random user generator API</a>中加载一个包含20个随机用户的列表，当组件在 <code>componentDidMount</code>  方法中加载时，如下所示：</p>
<pre><code class="language-js">componentDidMount() {
  axios
    .get("https://randomuser.me/api/?page=0&amp;results=20")
    .then((response) =&gt; this.setState({ users: response.data.results }))
    .catch((error) =&gt; console.log(error));
}
</code></pre>
<p>一旦我们获取了那些用户，我们会设置它为 <code>users</code>  状态，并在UI上显示它。</p>
<pre><code class="language-jsx">{users.map((user) =&gt; (
  &lt;User key={user.login.uuid} name={user.name} email={user.email} /&gt;
))}
</code></pre>
<p>在这里，我们将需要显示的所有数据传递给 <code>User</code>  组件。</p>
<p><code>User</code>  组件像这样：</p>
<pre><code class="language-jsx">const User = (props) =&gt; {
  const { name, email } = props;
  const { first, last } = name;

  return (
    &lt;div&gt;
      &lt;p&gt;
        Name: {first} {last}
      &lt;/p&gt;
      &lt;p&gt;Email: {email} &lt;/p&gt;
      &lt;hr /&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p><strong>这个<code>User</code>  组件是一个函数组件。</strong></p>
<p>函数组件是以大写字母开头并返回JSX的函数。</p>
<p>无论组件是类组件还是函数组件，都要记住以像 <code>User</code>  这样的大写字母开头。这就是React在使用 <code>&lt;User /&gt;</code>等普通HTML元素时将其与普通HTML元素区别开来的原因。</p>
<p>如果我们使用 <code>&lt;user /&gt;</code> ，React将检查名称为user的HTML元素。因为没有这样的HTML元素，所以您不会得到想要的输出。</p>
<p>在上面的 <code>User</code> 函数组件中，我们将props传递给函数的props参数中的组件。</p>
<p>所以和在类组件中不用 <code>this.props</code> 一样，我们只使用 <code>props</code> 。</p>
<p>我们从不在函数组件中使用 <code>this</code> 关键字，因此避免了与此绑定相关的各种问题。</p>
<p>因此，函数组件优先于类组件。</p>
<p>一旦我们有了 <code>props</code> ，我们就会使用对象解构语法来获取其中的值并显示在UI上。</p>
<h2 id="reacthooks">如何在React Hooks中使用状态</h2>
<p>从版本16.8.0开始，React引入了钩子。它们完全改变了我们在React中编写代码的方式。使用React hook，我们可以在函数组件中使用状态和生命周期方法。</p>
<blockquote>
<p>React钩子是添加了状态和生命周期方法的函数组件。<br>
所以现在,类组件和函数组件之间没有什么区别。</p>
</blockquote>
<p>它们都可以有状态和生命周期方法。</p>
<p>但是，现在人们在写 React 组件时，React hook 更常用，因为它们使代码更短，更容易理解。</p>
<p>现在，你很少会发现使用类组件编写的 React 组件。</p>
<p>要使用React hook声明状态，我们需要使用 <code>useState</code>  钩子。</p>
<p><code>useState</code>  钩子接受一个参数，该参数是状态的初始值。</p>
<p>在类组件中，状态总是一个对象。但是在使用 <code>useState</code>   时，可以提供任何值作为初始值，如number, string, boolean, object, array, null等。</p>
<p><code>useState</code>  钩子返回一个数组，它的第一个值是当前状态的值。第二个值是我们将用于更新状态的函数，类似于 <code>setState</code>  方法。</p>
<p>让我们举一个使用状态的类组件的例子。我们将使用钩子将其转换为函数组件。</p>
<pre><code class="language-jsx">import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    counter: 0
  };

  handleOnClick = () =&gt; {
    this.setState(prevState =&gt; ({
      counter: prevState.counter + 1
    }));
  };

  render() {
    return (
      &lt;div&gt;
        &lt;p&gt;Counter value is: {this.state.counter} &lt;/p&gt;
        &lt;button onClick={this.handleOnClick}&gt;Increment&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'));
</code></pre>
<p>这是使用类组件写的 <a href="https://codesandbox.io/s/delicate-thunder-xdpri?file=/src/index.js">代码示例</a> 。</p>
<p>让我们将上面的代码转换为使用钩子。</p>
<pre><code class="language-jsx">import React, { useState } from "react";
import ReactDOM from "react-dom";

const App = () =&gt; {
  const [counter, setCounter] = useState(0);

  return (
    &lt;div&gt;
      &lt;div&gt;
        &lt;p&gt;Counter value is: {counter} &lt;/p&gt;
        &lt;button onClick={() =&gt; setCounter(counter + 1)}&gt;Increment&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

ReactDOM.render(&lt;App /&gt;, document.getElementById("root"));
</code></pre>
<p>这是使用React钩子写的<a href="https://codesandbox.io/s/elegant-heyrovsky-3qco5?file=/src/index.js">代码示例</a>。</p>
<p>如您所见，使用React钩子使代码更简短，更容易理解。</p>
<p>让我们理解一下上面的代码：</p>
<ul>
<li>为了使用 <code>useState</code> 钩子，我们需要像第一行那样导入它。</li>
<li>在App组件内部，我们通过传递0作为初始值并使用解构语法来调用 <code>useState</code>  。我们将 <code>useState</code>  返回的数组值存储到 <code>counter</code>和 <code>setCounter</code>  变量中。</li>
<li>常用的约定是在用于更新状态的函数名前加上 <code>setCounter</code> 中的 <code>set</code>  关键字。</li>
<li>当单击递增按钮时，我们定义了一个内联函数，并通过传递更新的计数器值来调用 <code>setCounter</code>  函数。</li>
<li>注意，因为我们已经有了计数器的值，所以我们使用 <code>setCounter(counter + 1)</code> 来增加计数器的值。</li>
<li>由于内联on click处理程序中只有一条语句，所以不需要将代码移动到单独的函数中。不过，如果处理程序内部的代码变得复杂，您可以这样做。<br>
如果您想了解关于useState和其他React hook的更多细节(以及示例)，请查看我的React hook介绍文章。</li>
</ul>
<h3 id="thanksforreading">Thanks for reading!</h3>
<p>想要详细了解所有ES6+特性，包括let和const，promises、各种promise方法，数组和对象解构，箭头函数、async/await、导入和导出，以及更多？</p>
<p>请查阅我的《掌握现代JavaScript（<a href="https://modernjavascript.yogeshchavan.dev/">Mastering Modern JavaScript</a>）》一书。这本书涵盖了学习React的所有先决条件，并帮助您在JavaScript和React方面变得更好。</p>
<blockquote>
<p>在 <a href="https://www.freecodecamp.org/news/learn-modern-javascript/">这里</a> 查看这本书的免费预览内容。</p>
</blockquote>
<p>此外，您可以查看我的<strong>免费</strong><a href="https://yogeshchavan.podia.com/react-router-introduction">介绍React Router</a>课程，从零学习React Router。</p>
<p>想订阅 JavaScript, React, Node.js相关内容的日常更新？<a href="https://www.linkedin.com/in/yogesh-chavan97/">在领英关注我</a>。</p>
<p><a href="https://bit.ly/3w0DGum"><img src="https://gist.github.com/myogeshchavan97/98ae4f4ead57fde8d47fcf7641220b72/raw/c3e4265df4396d639a7938a83bffd570130483b1/banner.jpg" alt="banner" width="600" height="400" loading="lazy"></a></p>
<!--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[ 如何给恺撒密码编程：基本加密简介 ]]>
                </title>
                <description>
                    <![CDATA[ 恺撒密码是早期加密的一个著名实践。它会根据字母表上设定的密钥对句子进行重组加密。举个例子，密钥为3，取一个句子“I like to wear hats.”。 当这个句子使用密钥3加密后，它变成了： L olnh wr zhdu kdwv. 这让它很难阅读并且不被查觉地传递。 虽然这是一个非常简单的加密案例，但对于学习编码的人来说，它是一个完美的练习项目。 理解加密 为了实现这个代码，至少在JAVA里，你需要思考实际要做些什么。所以，让我们看看必要的编码步骤。 步骤 1：识别句子中的字符 步骤 2：找到字符在字母表中的位置 步骤 3：识别字符位置+密钥key后的位置 注意* 如果字符位置 + key > 26, 那么要从字母表的第1个字符继续循环。 步骤 4：用新字符代替原来的字符，生成一个新句子 步骤 5：重复直到达到句子原来的长度（for 循环） 步骤 6：返回结果 编码加密 当我们清楚要遵循哪些绝佳的步骤后，我们应该想一想编码时要做什么。 步骤 0：建立一个可以读取信息和密钥的函数 就像这样： public String Encrypt(String ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption/</link>
                <guid isPermaLink="false">63638bc473e419079177bda1</guid>
                
                    <category>
                        <![CDATA[ 编程 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keren Ma ]]>
                </dc:creator>
                <pubDate>Thu, 03 Nov 2022 09:49:30 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/11/0_tuogeHoQ53SQACY-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption-3bf77b4e19f7/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to code the Caesar Cipher: an introduction to basic encryption</a>
      </p><!--kg-card-begin: markdown--><p>恺撒密码是早期加密的一个著名实践。它会根据字母表上设定的密钥对句子进行重组加密。举个例子，密钥为3，取一个句子“I like to wear hats.”。</p>
<p>当这个句子使用密钥3加密后，它变成了：</p>
<p>L olnh wr zhdu kdwv.</p>
<p>这让它很难阅读并且不被查觉地传递。</p>
<p>虽然这是一个非常简单的加密案例，但对于学习编码的人来说，它是一个完美的练习项目。</p>
<h2 id="">理解加密</h2>
<p>为了实现这个代码，至少在JAVA里，你需要思考实际要做些什么。所以，让我们看看必要的编码步骤。</p>
<p>步骤 1：识别句子中的字符</p>
<p>步骤 2：找到字符在字母表中的位置</p>
<p>步骤 3：识别字符位置+密钥key后的位置</p>
<p>注意* 如果字符位置 + key &gt; 26, 那么要从字母表的第1个字符继续循环。</p>
<p>步骤 4：用新字符代替原来的字符，生成一个新句子</p>
<p>步骤 5：重复直到达到句子原来的长度（for 循环）</p>
<p>步骤 6：返回结果</p>
<h2 id="">编码加密</h2>
<p>当我们清楚要遵循哪些绝佳的步骤后，我们应该想一想编码时要做什么。</p>
<p>步骤 0：建立一个可以读取信息和密钥的函数</p>
<p>就像这样：</p>
<pre><code>public String Encrypt(String message, int key) {

}
</code></pre>
<p>步骤 1：识别句子中的字符</p>
<p>为此，我们需要建立一张字母表用来查找字符。</p>
<p>创建一个包含26个字母的变量“alphabet”。</p>
<pre><code>String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String alphabet2 = alphabet.toLowerCase();
</code></pre>
<p>步骤 2：找到字符在字母表中的位置</p>
<p>创建一个for循环来遍历消息中的每个字符。创建一个StringBuilder可以更便于我们来做这件事。</p>
<pre><code>StringBuilder encrypted = new StringBuilder(message);
for (int q = 0; q &lt; encrypted.length(); q++) {
    char currchar = encrypted.charAt(q);
    int index = alphabet.indexOf(currchar);
}
</code></pre>
<p>与此同时，我们要确保每个位置是一个字母。</p>
<pre><code>if (index != -1) {

}    
</code></pre>
<p>步骤 3：识别字符位置+密钥key后的位置</p>
<p>如果识别出字符是一个字母，那么我们要在修改后的字母表中找到它的位置。因此，我们需要建立一个修改后的字母表。</p>
<p>步骤 4：用新字符代替原来的字符，生成一个新句子</p>
<p>一旦我们在修改后的字母中找到了相应的值，我们应该将它设置到我们创建的StringBuilder中的相同位置。</p>
<pre><code>public String Encryption(String input, int key){
    StringBuilder encrypted = new StringBuilder(input);
    String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";        
    String alphabet2 = alphabet.toLowerCase();
    String keyedalphabet = alphabet.substring(key) + alphabet.substring(0, key);
    for (int q = 0; q &lt; encrypted.length(); q++) {
        char currChar = encrypted.charAt(q);
        int index = alphabet.indexOf(currChar);
        if (index != -1) {
        char newChar = keyedalphabet.charAt(index);
        encrypted.setCharAt(q, newChar);
        }
    }
}
</code></pre>
<p>步骤 5：重复直到达到句子原来的长度（for 循环）</p>
<p>现在，我们已经检查了字符是否为大写，但我们还需要检查字符是否为小写。为此，我们需要访问之前建立的alphabet2。</p>
<pre><code>index = alphabet2.indexOf(currChar);
if (index != -1) {
    String keyedalphabet2 = keyedalphabet.toLowerCase();
    char newChar = keyedalphabet2.charAt(index);
    encrypted.setCharAt(q, newChar);
}

</code></pre>
<p>步骤 6：返回结果</p>
<p>现在，我们已经完成了For循环。剩下的就是退出循环并返回String。</p>
<pre><code>public String Encryption(String input, int key){
    StringBuilder encrypted = new StringBuilder(input);
    String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String alphabet2 = alphabet.toLowerCase();
    String keyedalphabet = alphabet.substring(key) + alphabet.substring(0, key);
    for (int q = 0; q &lt; encrypted.length(); q++) {
        char currChar = encrypted.charAt(q);
        int index = alphabet.indexOf(currChar);
        if (index != -1) {
            char newChar = keyedalphabet.charAt(index);
            encrypted.setCharAt(q, newChar);
        }
        index = alphabet2.indexOf(currChar);
        if (index != -1) {
            String keyedalphabet2 = keyedalphabet.toLowerCase();
            char newChar = keyedalphabet2.charAt(index);
            encrypted.setCharAt(q, newChar);
        }
    }
    return encrypted
}
</code></pre>
<p>步骤 7：调试</p>
<p>但是等等，不对！encrypted不是一个字符串，它是一个StringBuilder，这个函数特别要求返回一个字符串！</p>
<p>幸运的是，有一个非常简单的函数可以纠正这种疏忽。</p>
<pre><code class="language-java">public String Encryption(String input, int key){
    StringBuilder encrypted = new StringBuilder(input);
    String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String alphabet2 = alphabet.toLowerCase();
    String keyedalphabet = alphabet.substring(key) + alphabet.substring(0, key);
    for (int q = 0; q &lt; encrypted.length(); q++) {
        char currChar = encrypted.charAt(q);
        int index = alphabet.indexOf(currChar);
        if (index != -1) {
            char newChar = keyedalphabet.charAt(index);
            encrypted.setCharAt(q, newChar);
        }
        index = alphabet2.indexOf(currChar);
        if (index != -1) {
            String keyedalphabet2 = keyedalphabet.toLowerCase();
            char newChar = keyedalphabet2.charAt(index);
            encrypted.setCharAt(q, newChar);
        }
    }
    return encrypted.toString();
}
</code></pre>
<p>这就是你如何得到原始句子的加密版本的方法。自己试试吧！</p>
<p>感谢你阅读本文！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何用 JavaScript 搭建一个模态框 ]]>
                </title>
                <description>
                    <![CDATA[ 你可能曾经遇到过这样的情况：你无意中试图在网页上执行一个操作，但幸运的是弹出一个窗口，要求你确认你的决定。 这个弹出窗口叫作“模态框”。它是一个弹出并显示在其他页面内容前面的网页元素。 你可以使用模态框来做一些事情，比如存储你不想立即在网页上看到的信息、创建导航菜单、添加召唤行动元素，等等。 一个很好的例子是，当你试图关闭推文编辑菜单时，Twitter 上出现的弹框。 你还可以将模态框用于其他事情，如创建召唤行动元素、导航菜单、通讯部件等。 作为一名 Web 开发人员，知道如何构建一个模态框是个很方便的技能。在本教程中，我将介绍使用 HTML、CSS 和 JavaScript 创建一个简单模态的过程。 以下是我们将制作的内容截图： 这些步骤很容易遵循，这样你就可以自定义创建或从头创建一个模态框——这完全取决于你。在本文的最后，我将提供 CodePen 文件供你练习。 步骤 1：添加标签 让我们从 HTML 开始吧。 首先，你要添加一个section元素，并赋予它两个类，modal和hidden。在这个元素下，你还会有一个div元素，它的类是overlay和hi ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-build-a-modal-with-javascript/</link>
                <guid isPermaLink="false">63564fb5509503074debe510</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keren Ma ]]>
                </dc:creator>
                <pubDate>Thu, 20 Oct 2022 08:18:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/10/meta-data-for-spa-seo.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-build-a-modal-with-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Modal with JavaScript</a>
      </p><!--kg-card-begin: markdown--><p>你可能曾经遇到过这样的情况：你无意中试图在网页上执行一个操作，但幸运的是弹出一个窗口，要求你确认你的决定。</p>
<p>这个弹出窗口叫作“模态框”。它是一个弹出并显示在其他页面内容前面的网页元素。</p>
<p>你可以使用模态框来做一些事情，比如存储你不想立即在网页上看到的信息、创建导航菜单、添加召唤行动元素，等等。</p>
<p>一个很好的例子是，当你试图关闭推文编辑菜单时，Twitter 上出现的弹框。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/twitter_warning_message_modal.png" alt="推特警告消息模态框" width="600" height="400" loading="lazy"></p>
<p>你还可以将模态框用于其他事情，如创建召唤行动元素、导航菜单、通讯部件等。</p>
<p>作为一名 Web 开发人员，知道如何构建一个模态框是个很方便的技能。在本教程中，我将介绍使用 HTML、CSS 和 JavaScript 创建一个简单模态的过程。</p>
<p>以下是我们将制作的内容截图：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/modal.png" alt="一个用 HTML、CSS、JavaScript 创建的模态框" width="600" height="400" loading="lazy"></p>
<p>这些步骤很容易遵循，这样你就可以自定义创建或从头创建一个模态框——这完全取决于你。在本文的最后，我将提供 CodePen 文件供你练习。</p>
<h2 id="1">步骤 1：添加标签</h2>
<p>让我们从 HTML 开始吧。</p>
<p>首先，你要添加一个<code>section</code>元素，并赋予它两个类，<code>modal</code>和<code>hidden</code>。在这个元素下，你还会有一个<code>div</code>元素，它的类是<code>overlay</code>和<code>hidden</code>。最后，添加一个<code>&lt;button&gt;</code>按钮元素，类为<code>btn</code>和<code>btn-open</code>。</p>
<p>就像这样：</p>
<pre><code class="language-html">&lt;section class="modal hidden"&gt;&lt;/section&gt;
&lt;div class="overlay hidden"&gt;&lt;/div&gt;
&lt;button class="btn btn-open"&gt;Open Modal&lt;/button&gt;
</code></pre>
<ul>
<li>
<p>带有<code>modal</code>的类的<code>section</code>将用于模态框的容器。</p>
</li>
<li>
<p>带有<code>overlay</code>类的<code>div</code>将作为覆盖的元素。这是模态框打开时你看到的黑色模糊背景。</p>
</li>
<li>
<p>类为<code>btn</code>和<code>btn-open</code>的按钮将作为你的模态框按钮，当你点击按钮时，打开模态框。</p>
</li>
</ul>
<p>现在在你的模态框中添加标记，并添加 “X” 按钮来关闭模态框。这个按钮的类为<code>btn-close</code>。</p>
<p>最后你的标记文本会像下面这样：</p>
<pre><code class="language-html">&lt;section class="modal hidden"&gt;
  &lt;div class="flex"&gt;
    &lt;img src="user.png" width="50px" height="50px" alt="user" /&gt;
    &lt;button class="btn-close"&gt;⨉&lt;/button&gt;
  &lt;/div&gt;
  &lt;div&gt;
    &lt;h3&gt;Stay in touch&lt;/h3&gt;（保持联系）
    &lt;p&gt;
      This is a dummy newsletter form so don't bother trying to test it. Not
      that I expect you to, anyways.:)（这是一个虚拟的通讯表单，所以不要费心去测试它。反正我也不指望你这么做）
    &lt;/p&gt;
  &lt;/div&gt;

  &lt;input type="email" id="email" placeholder="brendaneich@js.com" /&gt;
  &lt;button class="btn"&gt;Submit&lt;/button&gt;
&lt;/section&gt;

&lt;div class="overlay hidden"&gt;&lt;/div&gt;
&lt;button class="btn btn-open"&gt;Open Modal&lt;/button&gt;
</code></pre>
<p><strong>注意：</strong> 添加到模态框和覆盖元素的<code>hidden</code>类。这是非常重要的，因为你将使用 CSS 针对这些类来隐藏你的模态框和覆盖层。</p>
<p>这里是输出的结果：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/complete-markup.png" alt="complete-markup" width="600" height="400" loading="lazy"></p>
<h2 id="2">步骤 2：设置模态框的样式</h2>
<p>让我们从重置页面所有元素的默认内外边距开始，之后把模态框和打开模态框的按钮都居中对齐。</p>
<p>现在跳转到 CSS 页面添加这些样式：</p>
<pre><code class="language-css">* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Inter", sans-serif;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #222;
  position: relative;
  min-height: 100vh;
}
</code></pre>
<p>下一步是设置模态框本身和内部元素的样式。这个过程有些冗长，所以我将直接复制粘贴样式并作简要说明。</p>
<pre><code class="language-css">.modal {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.4rem;
  width: 450px;
  padding: 1.3rem;
  min-height: 250px;
  position: absolute;
  top: 20%;
  background-color: white;
  border: 1px solid #ddd;
  border-radius: 15px;
}

.modal .flex {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.modal input {
  padding: 0.7rem 1rem;
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 0.9em;
}

.modal p {
  font-size: 0.9rem;
  color: #777;
  margin: 0.4rem 0 0.2rem;
}

button {
  cursor: pointer;
  border: none;
  font-weight: 600;
}

.btn {
  display: inline-block;
  padding: 0.8rem 1.4rem;
  font-weight: 700;
  background-color: black;
  color: white;
  border-radius: 5px;
  text-align: center;
  font-size: 1em;
}

.btn-open {
  position: absolute;
  bottom: 150px;
}

.btn-close {
  transform: translate(10px, -20px);
  padding: 0.5rem 0.7rem;
  background: #eee;
  border-radius: 50%;
}
</code></pre>
<p>这里是输出的结果：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/complete-modal-style.png" alt="complete-modal-style" width="600" height="400" loading="lazy"></p>
<p>刚才你设置了模态框元素的样式，和绝对定位。定位起作用的原因是之前给 body 元素设置了相对定位。</p>
<p>你也给模态框内部的元素设置了样式，但是我不会更深地解释细节，因为这部分不是我们的重要内容。</p>
<h2 id="3overlay">步骤 3：添加覆盖层overlay</h2>
<p>对于覆盖层，你想让它在整个页面上有一个微妙的黑色背景和模糊。</p>
<p>由于你已经给 body 设置了相对定位，所以你可以用固定定位让 overlay 覆盖在 body 上。宽和高要设置为视窗的 100%。</p>
<pre><code class="language-css">.overlay {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(3px);
  z-index: 1;
}
</code></pre>
<p>这里是输出效果：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/overlay.png" alt="overlay" width="600" height="400" loading="lazy"></p>
<p>如果你只想让 overlay 只覆盖 body 而不覆盖模态框，你需要让模态框 modal 处在更高的<code>z-index</code>。</p>
<pre><code class="language-css">.modal {
  z-index: 2;
}
</code></pre>
<p>现在模态框将在 overlay 的上层而不被覆盖。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/modal-1.png" alt="modal" width="600" height="400" loading="lazy"></p>
<p>到这里，你已经成功地创建了模态框并在它下面添加了一个覆盖层！但现在你并不想显示模态框，至少在点击<code>open-modal</code>按钮之前不要显示。</p>
<p>为了隐藏模态框，你需要让添加了<code>.hidden</code>的类（模态框和覆盖层）不显示。</p>
<pre><code class="language-css">.hidden {
  display: none;
}
</code></pre>
<p>现在，页面中只有按钮显示。现在你可以在 JavaScript 中为模态框的功能做些事情了。</p>
<h2 id="4">步骤 4：模态框的功能</h2>
<p>在我们继续之前，我认为最好解释一下模态框是如何工作的。还记得你如何使用 <code>hidden</code> 类来隐藏模态框和覆盖层吗？要从元素中添加或删除这个类，需要使用 DOM 的 classList 元素。</p>
<p>但是，首先，你需要使用 DOM 的<code>querySelector</code> 方法选中并把它们存在变量里，以便重复使用。</p>
<pre><code class="language-js">const modal = document.querySelector(".modal");
const overlay = document.querySelector(".overlay");
const openModalBtn = document.querySelector(".btn-open");
const closeModalBtn = document.querySelector(".btn-close");
</code></pre>
<h2 id="">如何打开模态框</h2>
<p>为了显示模态框，创建一个函数 <code>openModal</code>。在这个函数中，你将使用 DOM <code>classList</code> 的属性，它采用了不同的方法比如 <code>.remove()</code> 和 <code>.add()</code> 从模态框和覆盖层中移除 <code>hidden</code> 类。</p>
<pre><code class="language-js">const openModal = function () {
  modal.classList.remove("hidden");
  overlay.classList.remove("hidden");
};
</code></pre>
<p>然后你可以使用 <code>eventListener</code> 把这个函数绑定到打开模态框按钮 <code>openModalBtn</code> 上。这样，只要点击这个按钮，函数就会被执行，这将显示模态框。</p>
<pre><code class="language-js">openModalBtn.addEventListener("click", openModal);
</code></pre>
<p>现在，当你点击 <code>open modal</code> 按钮，它将把 <code>hidden</code> 样式移除，以便你能看到你的模态框。</p>
<p>这里是输出结果：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/open-modal.gif" alt="Open modal" width="600" height="400" loading="lazy"></p>
<h2 id="">如何关闭模态框</h2>
<p>要关闭模态框，你也要创建一个函数 <code>closeModal</code>，在函数中，使用 <code>.add()</code> 方法把你移除的 <code>hidden</code> 添加回来。</p>
<p><code>classList</code> 也有一个 <code>add()</code> 方法，你可以用它在你点击 <code>closeModal</code> 时，把隐藏的样式加回来。就像你给打开按钮添加一个 <code>eventListener</code>，你还需要把函数绑定给<code>X</code>按钮关闭模态框——但现在，你要把<code>hidden</code>样式加回来。</p>
<pre><code class="language-js">const closeModal = function () {
  modal.classList.add("hidden");
  overlay.classList.add("hidden");
};
</code></pre>
<p>要关闭模态框，在关闭模态框按钮中添加一个 <code>eventListener</code> 来执行你刚刚编写的函数。</p>
<pre><code class="language-js">closeModalBtn.addEventListener("click", closeModal);
</code></pre>
<p>现在，当你点击关闭按钮时，该函数将把隐藏样式添加回模态框和覆盖层组件，从而关闭模态框。</p>
<p>这里是输出结果：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/close_modal.gif" alt="close modal" width="600" height="400" loading="lazy"></p>
<p>通常情况下，当你在模态框容器外部或网页主体上点击时，模态框也会关闭。为此，添加一个<code>eventListener</code>，在你点击覆盖层时关闭模态框。</p>
<pre><code class="language-js">overlay.addEventListener("click", closeModal);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/close_modal_when_overlay_is_clicked.gif" alt="close_modal_when_overlay_is_clicked" width="600" height="400" loading="lazy"></p>
<h2 id="">如何在按下键盘时关闭模态框</h2>
<p>除了在单击关闭按钮或覆盖层时关闭模态框外，你还可以附加一个事件侦听器来监视键盘事件。</p>
<p>在本例中，你可以希望在按下 “Escape” 键时关闭模态框，这与 Twitter compose 模态框示例非常相似。</p>
<pre><code class="language-js">document.addEventListener("keydown");
</code></pre>
<p>但是这次你想要的事件类型不是 <code>click</code> 事件——你想使用 <code>keydown</code> 事件来执行你的函数。</p>
<p>接下来，你将编写一个条件，检查当前按下的键是否为<code>Escape</code>键，模态框是否包含<code>hidden</code>类。现在它打开了，你想执行 <code>closemmodal</code> 函数（本质上就是关闭模态窗口）。</p>
<pre><code class="language-js">document.addEventListener("keydown", function (e) {
  if (e.key === "Escape" &amp;&amp; !modal.classList.contains("hidden")) {
    modalClose();
  }
});
</code></pre>
<p>当模态框打开时，你点击<kbd>Esc</kbd>键，它会关闭模态框。</p>
<p>有了这个，你已经成功地用 HTML、CSS 和 JavaScript 创建了一个模态组件，它就像预期的那样工作。🥳</p>
<p>下面是测试模态框效果的 CodePen 文件：</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe id="cp_embed_zYjjzoV" src="https://codepen.io/evavic44/embed/preview/zYjjzoV?default-tabs=css%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=zYjjzoV" title="Modal with overlay and blur" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;" loading="lazy"></iframe></figure><!--kg-card-begin: markdown--><h2 id="">总结</h2>
<p>我真诚地希望你觉得这篇文章有趣或有用。如果你喜欢，请与你的朋友分享或订阅我的博客，这样你就不会错过我之后发布的任何帖子。感谢你阅读本文。</p>
<p><a href="https://github.com/evavic44">GitHub</a> | <a href="https://twitter.com/victorekea">Twitter</a> | <a href="https://eke.hashnode.dev">Blog</a> | <a href="https://victoreke.com">Portfolio</a></p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
