<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>React on LGiki&#39;s Blog</title>
    <link>https://lgiki.net/tags/react/</link>
    <description>Recent content in React on LGiki&#39;s Blog</description>
    <generator>Hugo -- 0.148.2</generator>
    <language>en</language>
    <lastBuildDate>Wed, 15 Mar 2023 00:00:00 +0800</lastBuildDate>
    <atom:link href="https://lgiki.net/tags/react/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>翻译：Common Beginner Mistakes with React</title>
      <link>https://lgiki.net/post/common-beginner-mistakes-with-react/</link>
      <pubDate>Wed, 15 Mar 2023 00:00:00 +0800</pubDate>
      <guid>https://lgiki.net/post/common-beginner-mistakes-with-react/</guid>
      <description>翻译：Common Beginner Mistakes with React</description>
      <content:encoded><![CDATA[<h1 id="intro">Intro</h1>
<p>最近看了一篇文章：<a href="https://www.joshwcomeau.com/react/common-beginner-mistakes/">Common Beginner Mistakes with React</a>，讲的是 React 新手可能会犯的错误，文章写得很好，确实有很多我学习 React 初期犯过的错误，并且文中给了非常详细的代码和样例，因此将其翻译为中文，也作为自己的笔记。推荐看得懂英文的直接查看原文。</p>
<p>本文是对原文的翻译，稍微简略了原文中的一些描述，对于部分内容添加了补充信息，版权归原作者 <a href="https://twitter.com/JoshWComeau">Josh W Comeau</a> 所有。</p>
<h1 id="evaluating-with-zero">Evaluating with zero</h1>
<p>首先看看下面这段代码，你觉得页面上会显示什么呢？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">ShoppingList</span> <span class="nx">from</span> <span class="s1">&#39;./ShoppingList&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">items</span><span class="p">,</span> <span class="nx">setItems</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">length</span> <span class="o">&amp;&amp;</span> <span class="p">&lt;</span><span class="nt">ShoppingList</span> <span class="na">items</span><span class="o">=</span><span class="p">{</span><span class="nx">items</span><span class="p">}</span> <span class="p">/&gt;}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
</span></span></code></pre></div><p>答案是：会显示一个 <code>0</code>。</p>
<p>原因是 <code>items.length</code> 的计算结果为 0，而 0 是一个 False 的值，<code>&amp;&amp;</code> 的运算被短路，因此整个表达式就会解析为 0，因此上面那段代码本质上就等于：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>因此，在表达式里面应该使用纯正的布尔值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">items</span><span class="p">,</span> <span class="nx">setItems</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">ShoppingList</span> <span class="na">items</span><span class="o">=</span><span class="p">{</span><span class="nx">items</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>或者使用三元运算符：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">items</span><span class="p">,</span> <span class="nx">setItems</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">length</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span> <span class="p">&lt;</span><span class="nt">ShoppingList</span> <span class="na">items</span><span class="o">=</span><span class="p">{</span><span class="nx">items</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="kc">null</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="mutating-state">Mutating state</h1>
<p>假设现在要向购物车中添加新项目：</p>


<iframe src="https://codesandbox.io/embed/sandpack-project-7jxu1f?fontsize=14&hidenavigation=1&module=%2FApp.js&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="sandpack-project"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>


<p>每当用户提交新项目的时候，都会调用 <code>handleAddItem</code> 函数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleAddItem</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">items</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setItems</span><span class="p">(</span><span class="nx">items</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>但是这个函数不起作用，新的项目不会被添加到购物清单中，因为这违反了 React 中最神圣的规则：我们正在改变 state。</p>
<p>重点就是 <code>items.push(value)</code> 这一行，React 依赖于状态变量的 <strong>identity</strong> 来判断状态是否发生了变化。当我们向数组中 push 新的元素时，并没有改变该数组的 <strong>identity</strong>，因此 React 无法知道该值已经发生了变化。（译者注：可以理解为，当你向数组中添加了一个新元素之后，数组还是原来的那个数组，并没有生成一个新的数组，因此 React 无法判断该值已经发生了更改）</p>
<p>因此，如果要修复这个问题，我们需要创建一个全新的数组，就像这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleAddItem</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">nextItems</span> <span class="o">=</span> <span class="p">[...</span><span class="nx">items</span><span class="p">,</span> <span class="nx">value</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setItems</span><span class="p">(</span><span class="nx">nextItems</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>不修改现有数组，而是重新创建了一个新数组，它包含所有原数组的元素和新的元素（使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax"><code>...</code></a> 展开语法实现）。</p>
<p>这里的区别是编辑现有项与创建新项之间的区别。当我们将一个值传递给像 <code>setCount</code> 这样的状态设置函数时，它必须是一个新实体。</p>
<p>除了数组，对象也遵循同样的规律：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ❌ 修改一个现有的对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">handleChangeEmail</span><span class="p">(</span><span class="nx">nextEmail</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">user</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">nextEmail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setUser</span><span class="p">(</span><span class="nx">user</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 创建一个新对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">handleChangeEmail</span><span class="p">(</span><span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">nextUser</span> <span class="o">=</span> <span class="p">{</span> <span class="p">...</span><span class="nx">user</span><span class="p">,</span> <span class="nx">email</span><span class="o">:</span> <span class="nx">nextEmail</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setUser</span><span class="p">(</span><span class="nx">nextUser</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="not-generating-keys">Not generating keys</h1>
<p>你以前可能见过这样的警告：</p>
<pre tabindex="0"><code>Warning: Each child in a list should have a unique &#34;key&#34; prop.
</code></pre><p>例如这就是一个例子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">ShoppingList</span><span class="p">({</span> <span class="nx">items</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">item</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;{</span><span class="nx">item</span><span class="p">}&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">})}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">ShoppingList</span><span class="p">;</span>
</span></span></code></pre></div><p>这将导致以下错误：</p>
<pre tabindex="0"><code>Warning: Each child in a list should have a unique &#34;key&#34; prop.
Check the render method of `ShoppingList`. See https://reactjs.org/link/warning-keys for more information.
    at li
    at ShoppingList (https://sandpack-bundler.vercel.app/ShoppingList.js:17:3)
    at div
    at App (https://sandpack-bundler.vercel.app/App.js:26:42)
</code></pre><p>当我们渲染一个元素数组时，我们需要为 React 提供一些额外的上下文，以便它可以识别每个独立的元素。重要的是，这需要是一个唯一的标识符。</p>
<p>很多网上的文章都会建议使用数组索引来解决这个问题：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">ShoppingList</span><span class="p">({</span> <span class="nx">items</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">item</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">li</span> <span class="na">key</span><span class="o">=</span><span class="p">{</span><span class="nx">index</span><span class="p">}&gt;{</span><span class="nx">item</span><span class="p">}&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">})}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我不认为这是个好建议。这种方法有时会奏效，但在其他情况下可能会导致一些相当大的问题。</p>
<p>（译者注：作者这里没有详细说明会导致什么问题，其实在 React 官方的 docs 中有提到过这个问题：</p>
<blockquote>
<p>We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an <a href="https://robinpokorny.com/blog/index-as-a-key-is-an-anti-pattern/">in-depth explanation on the negative impacts of using an index as a key</a>. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.</p></blockquote>
<p>其中 Robin Pokorny 的那篇文章就有一个导致问题的例子）</p>
<p>因此，作者推荐在新的元素被添加到列表时，为其生成一个唯一的 ID：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleAddItem</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">nextItem</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">id</span><span class="o">:</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomUUID</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">label</span><span class="o">:</span> <span class="nx">value</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">nextItems</span> <span class="o">=</span> <span class="p">[...</span><span class="nx">items</span><span class="p">,</span> <span class="nx">nextItem</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setItems</span><span class="p">(</span><span class="nx">nextItems</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>crypto.randomUUID</code> 是浏览器内置的方法，在主流浏览器中都可以使用，这个方法会生成一个类似于 <code>d9bb3c4c-0459-48b9-a94c-7ca3963f7bd0</code> 的唯一字符串。</p>
<p>但是，千万不要在更新状态时生成 ID，例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">li</span> <span class="na">key</span><span class="o">=</span><span class="p">{</span><span class="nx">crypto</span><span class="p">.</span><span class="nx">randomUUID</span><span class="p">()}&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>这会导致每次渲染时，Key 都发生变化，Key 的变化会导致 React 销毁并重新创建这些元素，会对性能产生很大的负面影响。</p>
<p>例如从服务器获取数据时，可以通过以下方式为数据创建唯一的 ID：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">data</span><span class="p">,</span> <span class="nx">setData</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">retrieveData</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;/api/data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 这时候我们已经拿到了数据，接下来为每个元素生成一个 ID：
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">dataWithId</span> <span class="o">=</span> <span class="nx">json</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">item</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">id</span><span class="o">:</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomUUID</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 然后我们将 state 更新为带有 ID 的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">setData</span><span class="p">(</span><span class="nx">dataWithId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="missing-whitespace">Missing whitespace</h1>
<p>有以下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Welcome</span> <span class="nx">to</span> <span class="nx">Corpitech</span><span class="p">.</span><span class="nx">com</span><span class="o">!</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/login&#34;</span><span class="p">&gt;</span><span class="nx">Log</span> <span class="k">in</span> <span class="nx">to</span> <span class="k">continue</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
</span></span></code></pre></div><p>代码的运行结果：</p>


<iframe src="https://codesandbox.io/embed/sandpack-project-62k595?fontsize=14&hidenavigation=1&module=%2FApp.js&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="sandpack-project"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>


<p>这两个句子显示在了同一行，这是因为 JSX 编译器无法真正区分是语法上的空格还是我们为了缩进或代码可读性而添加的空格。</p>
<p>因此，我们需要在文本和 <code>a</code> 标签之间添加一个明确的空格符号：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Welcome</span> <span class="nx">to</span> <span class="nx">Corpitech</span><span class="p">.</span><span class="nx">com</span><span class="o">!</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="s1">&#39; &#39;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/login&#34;</span><span class="p">&gt;</span><span class="nx">Log</span> <span class="k">in</span> <span class="nx">to</span> <span class="k">continue</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>一个小提示：如果你使用 Prettier，它会自动为你添加这些空格字符！只需确保让它进行格式化（不要预先将内容拆分为多行）。</p>
<h1 id="accessing-state-after-changing-it">Accessing state after changing it</h1>
<p>有一个最小的计数器应用：</p>


<iframe src="https://codesandbox.io/embed/sandpack-project-j1o8o9?expanddevtools=1&fontsize=14&hidenavigation=1&module=%2FApp.js&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="sandpack-project"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>


<p>在点击按钮之后，我们将递增的 <code>count</code> 变量输出到 console，但奇怪的是，记录的值是错误的。
问题在于：React 中的状态设置函数（例如 <code>setCount</code>）是异步的。</p>
<p>这是有问题的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleClick</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">({</span> <span class="nx">count</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>很容易错误地认为 <code>setCount</code> 的功能类似于赋值，就好像它等同于：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">count</span> <span class="o">=</span> <span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">({</span> <span class="nx">count</span> <span class="p">});</span>
</span></span></code></pre></div><p>但在 React 中，当调用 <code>setCount</code> 时，我们并没有重新分配变量，只是安排了更新。</p>
<p>我们可能需要一些时间才能完全理解这个想法，但是这里有一些能帮助你理解的东西：我们无法重新分配 <code>count</code> 变量，因为它是一个常量！</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 使用的是 `const` 而不是 `let`，因此不能重新分配
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">count</span> <span class="o">=</span> <span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Uncaught TypeError:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                   <span class="c1">// Assignment to constant variable
</span></span></span></code></pre></div><p>那么如何解决这个问题？因为我们已经知道了这个值应该是多少，我们可以将其赋值给一个变量，并访问这个新变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleClick</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">nextCount</span> <span class="o">=</span> <span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setCount</span><span class="p">(</span><span class="nx">nextCount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 想引用新的值时，可以使用 `nextCount`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">({</span> <span class="nx">nextCount</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我喜欢使用 &ldquo;next&rdquo; 前缀来声明这类变量，例如 <code>nextCount</code>、<code>nextItems</code>、<code>nextEmail</code> 等，这让我更清楚我们不是在更新现有的值，而是在安排下一个值。</p>
<h1 id="returning-multiple-elements">Returning multiple elements</h1>
<p>有时，一个组件需要返回多个顶级元素。</p>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">LabeledInput</span><span class="p">({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">label</span> <span class="na">htmlFor</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">      <span class="na">id</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="na">...delegated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">LabeledInput</span><span class="p">;</span>
</span></span></code></pre></div><p>上面代码将得到以下报错：</p>
<pre tabindex="0"><code>/LabeledInput.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment &lt;&gt;...&lt;/&gt;? (6:4)
  4 |       {label}
  5 |     &lt;/label&gt;
&gt; 6 |     &lt;input
    |     ^
  7 |       id={id}
  8 |       {...delegated}
  9 |     /&gt;
</code></pre><p>这是因为 JSX 编译为普通的 JavaScript，上面的代码将会被编译为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">LabeledInput</span><span class="p">({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">&#39;label&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">htmlFor</span><span class="o">:</span> <span class="nx">id</span> <span class="p">},</span> <span class="nx">label</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">&#39;input&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="nx">id</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在 JavaScript 中，不允许像这样返回多个返回值，这就跟以下函数相同：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">addTwoNumbers</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;the answer is&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>那么如何解决这个问题呢？</p>
<p>在很长的一段时间里，标准的做法是将两个元素包装在一个 wrapper 标签中，例如 <code>&lt;div&gt;</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">LabeledInput</span><span class="p">({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">htmlFor</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">        <span class="na">id</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="na">...delegated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们可以使用 fragments 来让这个解决方案变得更好：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">LabeledInput</span><span class="p">({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">React.Fragment</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">htmlFor</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">        <span class="na">id</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="na">...delegated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">React.Fragment</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>React.Fragment</code> 是一个纯粹为了解决这个问题而存在的 React 组件，它允许在不影响 DOM 的情况下返回多个顶级元素，这意味着不需要再使用 <code>&lt;div&gt;</code> 来污染我们的文档。</p>
<p><code>&lt;React.Fragment&gt;</code> 还可以简写为 <code>&lt;&gt;</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">LabeledInput</span><span class="p">({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="p">...</span><span class="nx">delegated</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">htmlFor</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">        <span class="na">id</span><span class="o">=</span><span class="p">{</span><span class="nx">id</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="na">...delegated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我喜欢这里的象征意义：React 团队使用一个空的 HTML 标签 <code>&lt;&gt;</code> 来表明 Fragment 不会产生任何真正的 HTML 标签。</p>
<h1 id="flipping-from-uncontrolled-to-controlled">Flipping from uncontrolled to controlled</h1>
<p>下面的代码是一个典型的将一个输入绑定到一个 React 状态的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">email</span><span class="p">,</span> <span class="nx">setEmail</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">htmlFor</span><span class="o">=</span><span class="s">&#34;email-input&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Email</span> <span class="nx">address</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">        <span class="na">id</span><span class="o">=</span><span class="s">&#34;email-input&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">type</span><span class="o">=</span><span class="s">&#34;email&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">email</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="na">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">event</span> <span class="p">=&gt;</span> <span class="nx">setEmail</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
</span></span></code></pre></div><p>当你在这个 input 中输入内容的时候，你会收到以下控制台警告：</p>
<pre tabindex="0"><code>Warning: A component is changing an uncontrolled input to be controlled.
</code></pre><p>以下是解决方法：将 <code>email</code> 状态初始化为空字符串。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">email</span><span class="p">,</span> <span class="nx">setEmail</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span></code></pre></div><p>当我们设置 <code>value</code> 属性时，我们告诉 React 我们希望这是一个受控输入。但是，只有在我们传递一个已定义的值时才有效！通过将 <code>email</code> 初始化为空字符串，我们确保 <code>value</code> 永远不会被设置为 <code>undefined</code>。</p>
<h1 id="missing-style-brackets">Missing style brackets</h1>
<p>JSX 和 HTML 非常类似，但两者之间也存在一些令人惊讶的差异，让人措手不及。大多数差异在文档中都有详细记录，同时，console 中的警告信息也都很详细。例如，如果不小心写了 <code>class</code> 而不是 <code>className</code>，React 会准确告诉你问题出在哪里。</p>
<p>但是有一个差别容易使人误会：<code>style</code> 属性。</p>
<p>在 HTML 中，<code>style</code> 是一个字符串：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color: red; font-size: 1.25rem&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  Hello World
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>然而，在 JSX 中，<code>style</code> 属性需要是一个对象，并使用驼峰式的属性名称。</p>
<p>下面将展示一段有问题的代码，你能发现错误在哪吗：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">      <span class="na">style</span><span class="o">=</span><span class="p">{</span> <span class="nx">color</span><span class="o">:</span> <span class="s1">&#39;red&#39;</span><span class="p">,</span> <span class="nx">fontSize</span><span class="o">:</span> <span class="s1">&#39;1.25rem&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Hello</span> <span class="nx">World</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
</span></span></code></pre></div><p>错误提示：</p>
<pre tabindex="0"><code>/App.js: Unexpected token, expected &#34;}&#34; (6:19)
  4 |   return (
  5 |     &lt;button
&gt; 6 |       style={ color: &#39;red&#39;, fontSize: &#39;1.25rem&#39; }
    |                    ^
  7 |     &gt;
  8 |       Hello World
  9 |     &lt;/button&gt;
</code></pre><p>问题是，我们需要使用两个花括号：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">  <span class="err">//</span> <span class="na">使用</span> <span class="err">&#34;</span><span class="p">{{</span><span class="err">&#34;</span> <span class="na">而不是</span> <span class="err">&#34;</span><span class="p">{</span><span class="err">&#34;:</span>
</span></span><span class="line"><span class="cl">  <span class="na">style</span><span class="o">=</span><span class="p">{{</span> <span class="nx">color</span><span class="o">:</span> <span class="s1">&#39;red&#39;</span><span class="p">,</span> <span class="nx">fontSize</span><span class="o">:</span> <span class="s1">&#39;1.25rem&#39;</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Hello</span> <span class="nx">World</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>在 JSX 中，我们使用花括号来创建表达式槽（expression slot），我们可以在这个插槽中放置任何有效的 JS 表达式，例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="nx">isPrimary</span> <span class="o">?</span> <span class="s1">&#39;btn primary&#39;</span> <span class="o">:</span> <span class="s1">&#39;btn&#39;</span><span class="p">}&gt;</span>
</span></span></code></pre></div><p>任何放在 <code>{}</code> 之间的内容都将作为 <code>JavaScript</code> 来运行，最终取运行的结果作为值，因此上面代码的 <code>className</code> 将会是 <code>btn primary</code> 或 <code>btn</code>。</p>
<p>因此，对于 <code>style</code> 属性，我们首先需要创建一个表达式槽，然后将一个 JavaScript 对象传入这个槽中。</p>
<p>把对象作为一个变量可能会更清晰：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="c1">// 1. 创建一个 style 对象:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">btnStyles</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">color</span><span class="o">:</span> <span class="s1">&#39;red&#39;</span><span class="p">,</span> <span class="nx">fontSize</span><span class="o">:</span> <span class="s1">&#39;1.25rem&#39;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 将对象传入 style 属性:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">&lt;</span><span class="nt">button</span> <span class="na">style</span><span class="o">=</span><span class="p">{</span><span class="nx">btnStyles</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Hello</span> <span class="nx">World</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 或者，我们可以在一行中写完:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">&lt;</span><span class="nt">button</span> <span class="na">style</span><span class="o">=</span><span class="p">{{</span> <span class="nx">color</span><span class="o">:</span> <span class="s1">&#39;red&#39;</span><span class="p">,</span> <span class="nx">fontSize</span><span class="o">:</span> <span class="s1">&#39;1.25rem&#39;</span> <span class="p">}}&gt;</span>
</span></span></code></pre></div><p>其中，最外层的花括号创建了一个表达式槽，内层的花括号创建了一个 JS 对象来描述样式。</p>
<h1 id="async-effect-function">Async effect function</h1>
<p>假设有一个函数可以在 mount 的时候从 API 中获取用户数据，我们使用 <code>useEffect</code>，并使用 <code>await</code>，这是第一次尝试：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">API</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./constants&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">UserProfile</span><span class="p">({</span> <span class="nx">userId</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">user</span><span class="p">,</span> <span class="nx">setUser</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">React</span><span class="p">.</span><span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">API</span><span class="si">}</span><span class="sb">/get-profile?id=</span><span class="si">${</span><span class="nx">userId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">setUser</span><span class="p">(</span><span class="nx">json</span><span class="p">.</span><span class="nx">user</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">userId</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">user</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s1">&#39;Loading…&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">section</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">dl</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="o">&lt;</span><span class="nx">dt</span><span class="o">&gt;</span><span class="nx">Name</span><span class="o">&lt;</span><span class="err">/dt&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="o">&lt;</span><span class="nx">dd</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/dd&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="o">&lt;</span><span class="nx">dt</span><span class="o">&gt;</span><span class="nx">Email</span><span class="o">&lt;</span><span class="err">/dt&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="o">&lt;</span><span class="nx">dd</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/dd&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="err">/dl&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/section&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">UserProfile</span><span class="p">;</span>
</span></span></code></pre></div><p>这段代码将得到以下报错：</p>
<pre tabindex="0"><code>/UserProfile.js: &#39;await&#39; is only allowed within async functions (9:16)
   7 |   React.useEffect(() =&gt; {
   8 |     const url = `${API}/get-profile?id=${userId}`;
&gt;  9 |     const res = await fetch(url);
     |                 ^
  10 |     const json = await res.json();
  11 |    
  12 |     setUser(json.user);
</code></pre><p>那给函数加上 <code>async</code> 关键词呢：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">React</span><span class="p">.</span><span class="nx">useEffect</span><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">API</span><span class="si">}</span><span class="sb">/get-profile?id=</span><span class="si">${</span><span class="nx">userId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">setUser</span><span class="p">(</span><span class="nx">json</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[</span><span class="nx">userId</span><span class="p">]);</span>
</span></span></code></pre></div><p>还是不行：</p>
<pre tabindex="0"><code>destroy is not a function
</code></pre><p>解决方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">React</span><span class="p">.</span><span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 创建一个 async 函数...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">async</span> <span class="kd">function</span> <span class="nx">runEffect</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">API</span><span class="si">}</span><span class="sb">/get-profile?id=</span><span class="si">${</span><span class="nx">userId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setUser</span><span class="p">(</span><span class="nx">json</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ...然后调用它:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">runEffect</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[</span><span class="nx">userId</span><span class="p">]);</span>
</span></span></code></pre></div><p>要理解为什么需要这么写，需要考虑 <code>async</code> 关键词的实际作用，对于以下函数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">greeting</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="s2">&#34;Hello world!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>乍一看可能会以为它返回字符串 <code>&quot;Hello world!&quot;</code>，但实际上，这个函数返回一个 promise。这就是问题所在，因为 <code>useEffect</code> 并不希望我们返回一个 promise，它希望我们要么什么都不返回，要么就返回一个清理函数。</p>
<h1 id="developing-an-intuition">Developing an intuition</h1>
<p>最后，作者最近推出了一个 React 的课程，课程页面做得非常炫酷：<a href="https://www.joyofreact.com/">https://www.joyofreact.com/</a></p>
<p>这个博主的文章质量一直很高，课程应该也不差，感兴趣的可以看看！</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
