薛总 發表於 2019-7-29 15:51:00

React Hook:使用 useEffect

<div class="post-header">
<h2 class="post-title">React Hook:使用 useEffect</h2>
</div>
<div class="post-content markdown-body">
<div class="index-menu">
<ul class="index-menu-list">
<li class="index-menu-item">一、描述</li>
<li class="index-menu-item">二、需要清理的副作用
<ul class="index-menu-list">
<li class="index-menu-item">1、在 class 组件中</li>
<li class="index-menu-item">2、使用 effect Hook 的示例
<ul class="index-menu-list">
<li class="index-menu-item">1、useEffect 做了什么?</li>
</ul>
</li>
<li class="index-menu-item">2、为什么在组件内调用 useEffect?</li>
<li class="index-menu-item">3、每次 render 之后都会执行 useEffect 吗?</li>
<li class="index-menu-item">3、详细代码拆分说明
<ul class="index-menu-list">
<li class="index-menu-item">Tip</li>
</ul>
</li>
</ul>
</li>
<li class="index-menu-item">三、需要清理的副作用
<ul class="index-menu-list">
<li class="index-menu-item">1、使用 class 组件示例:
<ul class="index-menu-list">
<li class="index-menu-item">注意</li>
</ul>
</li>
<li class="index-menu-item">2、使用 Hooks 的示例
<ul class="index-menu-list">
<li class="index-menu-item">1、为什么从 effect 中返回一个 function?</li>
</ul>
</li>
<li class="index-menu-item">2、React 在什么时候清理?</li>
</ul>
</li>
<li class="index-menu-item">四、总结</li>
<li class="index-menu-item">五、使用 effect 的 tips
<ul class="index-menu-list">
<li class="index-menu-item">1、Tips:使用多个 effect 来分离问题</li>
<li class="index-menu-item">2、说明:为什么 effect 在每次 update 都会运行</li>
<li class="index-menu-item">3、Tip:跳过 effect 优化性能
<ul class="index-menu-list">
<li class="index-menu-item">注意</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<p>&nbsp;</p>
<h2>一、描述</h2>
<p>Effect Hook 可以让你能够在 Function 组件中执行副作用(side effects):</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">import <span class="token punctuation">{ useState<span class="token punctuation">, useEffect <span class="token punctuation">} <span class="token keyword">from <span class="token string">'react'<span class="token punctuation">;
<span class="token keyword">function <span class="token function">Example<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token number">0<span class="token punctuation">)<span class="token punctuation">;
<span class="token comment">// Similar to componentDidMount and componentDidUpdate:
<span class="token comment">// 类似于 componentDidMount 和 ComponentDidUpdate
<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    <span class="token comment">// Update the document title using the browser API
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">return <span class="token punctuation">(
    <span class="token operator">&lt;div<span class="token operator">&gt;
      <span class="token operator">&lt;p<span class="token operator">&gt;You clicked <span class="token punctuation">{count<span class="token punctuation">} times<span class="token operator">&lt;<span class="token operator">/p<span class="token operator">&gt;
      <span class="token operator">&lt;button onClick<span class="token operator">=<span class="token punctuation">{<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token function">setCount<span class="token punctuation">(count <span class="token operator">+ <span class="token number">1<span class="token punctuation">)<span class="token punctuation">}<span class="token operator">&gt;
      Click me
      <span class="token operator">&lt;<span class="token operator">/button<span class="token operator">&gt;
    <span class="token operator">&lt;<span class="token operator">/div<span class="token operator">&gt;
<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>上面代码看过很多次了,是 React 文档中 Hook 部分一直使用的计数器示例,但是多了个新的功能:把文档标题设置为包含点击次数的自定义消息。而这就是一个副作用。</p>
<p>数据获取,设置订阅或者手动直接更改 React 组件中的 DOM 都属于副作用。有的人习惯成这种行为为&nbsp;<code>effects</code>,我是比较习惯叫&nbsp;<code>side effects</code>&nbsp;也就是副作用的, 这是个概念,需要在 React 必须习惯的概念。</p>
<p>如果熟悉 React 类声明周期方法,可以把&nbsp;<code>useEffect</code>&nbsp;Hook 视作&nbsp;<code>componentDidMount</code>、<code>componentDidUpdate</code>&nbsp;和&nbsp;<code>componentWillUnmount</code>&nbsp;的组合体。</p>
<p>React 组件中有两种常见的副作用:</p>
<ul>
<li>需要清理的副作用</li>
<li>不需要清理的副作用。</li>
</ul>
<h2>二、需要清理的副作用</h2>
<p>有的时候,我们希望在 React 更新 DOM 之后进行一些额外的操作。网络请求、手动更改 DOM 以及日志记录都是不需要清理的副作用的常见场景。因为运行之后,可以立即被销毁掉。</p>
<p>下面是在 class 组件和 function 组件中分别表示这两种副作用的使用方式:</p>
<h3>1、在 class 组件中</h3>
<p>在 React class 组件中, render 方法本身不应该进行副作用操作,但是我们通常是期望在 React 更新 DOM 之后执行一些有必要的副作用。</p>
<p>这就是为什么在 React class 中,会把副作用放在&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentDidUpdate</code>&nbsp;中。回到计数器的示例中,如果要在 class 计数器组件中实现上面的功能,则代码如下:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">class <span class="token class-name">Example <span class="token keyword">extends <span class="token class-name">React<span class="token punctuation">.Component <span class="token punctuation">{
<span class="token function">constructor<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">super<span class="token punctuation">(props<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.state <span class="token operator">= <span class="token punctuation">{
      count<span class="token punctuation">: <span class="token number">0
    <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidMount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">return <span class="token punctuation">(
      <span class="token operator">&lt;div<span class="token operator">&gt;
      <span class="token operator">&lt;p<span class="token operator">&gt;You clicked <span class="token punctuation">{<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token punctuation">} times<span class="token operator">&lt;<span class="token operator">/p<span class="token operator">&gt;
      <span class="token operator">&lt;button onClick<span class="token operator">=<span class="token punctuation">{<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token keyword">this<span class="token punctuation">.<span class="token function">setState<span class="token punctuation">(<span class="token punctuation">{ count<span class="token punctuation">: <span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count <span class="token operator">+ <span class="token number">1 <span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">}<span class="token operator">&gt;
          Click me
      <span class="token operator">&lt;<span class="token operator">/button<span class="token operator">&gt;
      <span class="token operator">&lt;<span class="token operator">/div<span class="token operator">&gt;
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>上面代码很明显,class 组件中,两个生命周期中有相同的代码(虽然 componentDidUpdate 中的内容也可以放在 click 的事件 handler 中)</p>
<p>这是因为在多数情况下,我们希望执行相同的副作用,无论是组件刚 mount 还是 update 之后。而从概念上来讲,我们希望他在每次 render 之后发生,但是 React 类组件是没有这种生命周期的。虽然可以把&nbsp;<code>document.title = 'You clicked' + this.state.count + ' times';</code>&nbsp;这个操作封装到一个方法中,但是还是需要在&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentDidUpdate</code>&nbsp;中调用两次。</p>
<h3>2、使用 effect Hook 的示例</h3>
<p>文章最顶部已经写了下面的示例,为了分析代码,单独再拿到这里:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">import <span class="token punctuation">{ useState<span class="token punctuation">, useEffect <span class="token punctuation">} <span class="token keyword">from <span class="token string">'react'<span class="token punctuation">;

<span class="token keyword">function <span class="token function">Example<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token number">0<span class="token punctuation">)<span class="token punctuation">;

<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;

<span class="token keyword">return <span class="token punctuation">(
    <span class="token operator">&lt;div<span class="token operator">&gt;
      <span class="token operator">&lt;p<span class="token operator">&gt;You clicked <span class="token punctuation">{count<span class="token punctuation">} times<span class="token operator">&lt;<span class="token operator">/p<span class="token operator">&gt;
      <span class="token operator">&lt;button onClick<span class="token operator">=<span class="token punctuation">{<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token function">setCount<span class="token punctuation">(count <span class="token operator">+ <span class="token number">1<span class="token punctuation">)<span class="token punctuation">}<span class="token operator">&gt;
      Click me
      <span class="token operator">&lt;<span class="token operator">/button<span class="token operator">&gt;
    <span class="token operator">&lt;<span class="token operator">/div<span class="token operator">&gt;
<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4>1、<code>useEffect</code>&nbsp;做了什么?</h4>
<p>通过使用这个 Hook,通知 React 组件需要在渲染后执行什么操作。React 将记住传递的 function(把这个 function 成为 “effect”),并在执行 DOM 更新后调用这个 function。在这个效果中,主要的功能仍旧是设置&nbsp;<code>document.title</code>,但是也可以执行数据获取,或者是调用其他的命令式的 API。</p>
<h3>2、为什么在组件内调用&nbsp;<code>useEffect</code>?</h3>
<p>在组件内使用&nbsp;<code>useEffect</code>&nbsp;是的可以直接从副作用中访问计数器的&nbsp;<code>count</code>&nbsp;或者任何的 props。不需要使用特殊的 API 来读取它,它已经在函数的范围内了(通过&nbsp;<code>useState</code>)。Hooks 拥抱 Javascript 的闭包,并且避免在 Javascript 已经提供解决方案的情况下在去引入特定的 React API。</p>
<h3>3、每次 render 之后都会执行 useEffect 吗?</h3>
<p>是的!</p>
<p>这是默认行为,在第一次 render 之后和每次 update 之后都会运行。你可能会更容易的认为副作用发生在 “render 之后”,而不是发生在 “mount” 和 “update” 之后。不过 React 保证 DOM 在运行时副作用已经更新。</p>
<p>(网络请求每次都放在这里面肯定是有问题的,因此需要定制)如果要定制&nbsp;<code>useEffect</code>&nbsp;的默认执行行为,可以参考:https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects</p>
<h3>3、详细代码拆分说明</h3>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">function <span class="token function">Example<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token number">0<span class="token punctuation">)<span class="token punctuation">;

<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>我们通过&nbsp;<code>useState</code>&nbsp;声明了&nbsp;<code>count</code>&nbsp;state 变量,并且通知 React 需要使用 effect。</p>
<p>然后把一个 funcrion 传递给&nbsp;<code>useEffect</code>&nbsp;Hook,而传递的这个 funcrion 就是副作用。</p>
<p>在我们的副作用中,使用&nbsp;<code>document.title</code>&nbsp;浏览器 API 设置文档的标题,可以在 effect 中读取最新的 count,因为 count 变量作用域就是在整个 Example function 中。当 React 渲染我们的组件时,会机主我们使用的 effect,然后在更新 DOM 后运行需要的下沟哦。每次渲染都会发生这样的情况,包括第一次 render。</p>
<p>你可能会注意到,传递给&nbsp;<code>useEffect</code>&nbsp;的 function 在每次 render 的时候有所不同,这是故意为之的。事实上,这就是让我们在副作用中读取 count 值而不需要担心这个值是旧值。每次在 re-render 的时候,都会有一个不同的副作用,来取代之前的副作用。在某种程度上,这使得副作用更像是 render 结果的一部分——每个副作用都“属于”特殊的 render。文章后面会提到为什么这是有用的。</p>
<h4>Tip</h4>
<p>与&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentDidUpdate</code>&nbsp;不同,使用&nbsp;<code>useEffect</code>&nbsp;调度的副作用不会阻塞浏览器更新屏幕。这使得 application 感觉上具有响应式。大多数副作用不需要同步发生。而如果需要同步进行,(比如测量布局),有一个单独的&nbsp;useLayoutEffect&nbsp;Hook, API 和&nbsp;<code>useEffect</code>&nbsp;相同。</p>
<h2>三、需要清理的副作用</h2>
<p>上面都是不需要清理的副作用,然而,有些副作用是需要去清理的。比如,肯呢过希望设置对某些外部数据源的 subscription。而在这种情况下,清理订阅是非常重要的,这样不会引入内存泄露。</p>
<h3>1、使用 class 组件示例:</h3>
<p>在 React class 中,通常会在&nbsp;<code>componentDidMount</code>&nbsp;中设置帝国与,而在&nbsp;<code>componentWillUnmount</code>&nbsp;中清楚它。比如有一个&nbsp;<code>ChatAPI</code>&nbsp;模块,可以订阅好友的在线状态,在 class 组件中可能如下所示:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">class <span class="token class-name">FriendStatus <span class="token keyword">extends <span class="token class-name">React<span class="token punctuation">.Component <span class="token punctuation">{
<span class="token function">constructor<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">super<span class="token punctuation">(props<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.state <span class="token operator">= <span class="token punctuation">{ isOnline<span class="token punctuation">: <span class="token keyword">null <span class="token punctuation">}<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.handleStatusChange <span class="token operator">= <span class="token keyword">this<span class="token punctuation">.<span class="token function">handleStatusChange<span class="token punctuation">.<span class="token function">bind<span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidMount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentWillUnmount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">handleStatusChange<span class="token punctuation">(<span class="token parameter">status<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">this<span class="token punctuation">.<span class="token function">setState<span class="token punctuation">(<span class="token punctuation">{
      isOnline<span class="token punctuation">: status<span class="token punctuation">.isOnline
    <span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">if <span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.isOnline <span class="token operator">=== <span class="token keyword">null<span class="token punctuation">) <span class="token punctuation">{
      <span class="token keyword">return <span class="token string">'Loading...'<span class="token punctuation">;
    <span class="token punctuation">}
    <span class="token keyword">return <span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.isOnline <span class="token operator">? <span class="token string">'Online' <span class="token punctuation">: <span class="token string">'Offline'<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>请注意&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentWillMount</code>&nbsp;需要相互对应。class 组件的生命周期强制我们去拆分这个逻辑,即使他们中的概念代码和相同的副作用是有关的。</p>
<h4>注意</h4>
<p>眼里好的人可能注意到了上面的示例可能需要一个&nbsp;<code>componentDidUpdate</code>&nbsp;才能完全的正确,目前暂时忽略。</p>
<h3>2、使用 Hooks 的示例</h3>
<p>一开始,可能会认为需要单独的 effect 去清理,但是添加订阅和删除订阅的代码联系非常紧密,因此&nbsp;<code>useEffect</code>&nbsp;旨在将它保持在一起。如果你的副作用返回一个方法,则 React 则在清理时运行:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">import <span class="token punctuation">{ useState<span class="token punctuation">, useEffect <span class="token punctuation">} <span class="token keyword">from <span class="token string">'react'<span class="token punctuation">;

<span class="token keyword">function <span class="token function">FriendStatus<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token keyword">null<span class="token punctuation">)<span class="token punctuation">;

<span class="token keyword">function <span class="token function">handleStatusChange<span class="token punctuation">(<span class="token parameter">status<span class="token punctuation">) <span class="token punctuation">{
    <span class="token function">setIsOnline<span class="token punctuation">(status<span class="token punctuation">.isOnline<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token comment">// Specify how to clean up after this effect:
    <span class="token keyword">return <span class="token keyword">function <span class="token function">cleanup<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
      ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;

<span class="token keyword">if <span class="token punctuation">(isOnline <span class="token operator">=== <span class="token keyword">null<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">return <span class="token string">'Loading...'<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">return isOnline <span class="token operator">? <span class="token string">'Online' <span class="token punctuation">: <span class="token string">'Offline'<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4>1、为什么从 effect 中返回一个 function?</h4>
<p>这是 effect 可选的清理机制。每个 effect 都可以返回一个在它之后清理的 function。这使得我们能够保持添加订阅和删除订阅彼此接近的订阅的逻辑。这同样是 effect 的一部分。</p>
<h3>2、React 在什么时候清理?</h3>
<p>当组件卸载的时候,React 会执行清理工作。</p>
<p>然而,effect 会针对每个 render 运行而不仅仅是一次,这就是 React 在下次运行 effect 之前还清除前一个 render effect 的原因。</p>
<p>有两个链接:</p>
<ul>
<li>为什么上述行为能够避免 bug</li>
<li>如何在出现性能问题时选择退出此行为</li>
</ul>
<h2>四、总结</h2>
<p>我们已经了解了&nbsp;<code>useEffect</code>&nbsp;能够在组件 render 之后进行不同类型的副作用。某些 effect 可能需要清理,因此可以在 effect 中返回一个 function:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"> <span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">return <span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
      ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>而有一些 side effect 可能没有清理的过程,因此不需要返回任何内容。</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>通过&nbsp;<code>useEffect</code>,能够将之前在两个生命周期中的内容整合到一个 function 中。</p>
<h2>五、使用 effect 的 tips</h2>
<h3>1、Tips:使用多个 effect 来分离问题</h3>
<p>使用 Hook 的动机中包括了 class 组件的生命周期将相关的逻辑拆分的问题的解决,而在 Hook 的使用中,也能够把多个 effect 放在 function 组件。</p>
<p>下面是 class 组件的相关代码:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">class <span class="token class-name">FriendStatusWithCounter <span class="token keyword">extends <span class="token class-name">React<span class="token punctuation">.Component <span class="token punctuation">{
<span class="token function">constructor<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">super<span class="token punctuation">(props<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.state <span class="token operator">= <span class="token punctuation">{ count<span class="token punctuation">: <span class="token number">0<span class="token punctuation">, isOnline<span class="token punctuation">: <span class="token keyword">null <span class="token punctuation">}<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.handleStatusChange <span class="token operator">= <span class="token keyword">this<span class="token punctuation">.<span class="token function">handleStatusChange<span class="token punctuation">.<span class="token function">bind<span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidMount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentWillUnmount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">handleStatusChange<span class="token punctuation">(<span class="token parameter">status<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">this<span class="token punctuation">.<span class="token function">setState<span class="token punctuation">(<span class="token punctuation">{
      isOnline<span class="token punctuation">: status<span class="token punctuation">.isOnline
    <span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>请注意设置&nbsp;<code>document.title</code>&nbsp;的逻辑如何在&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentDidUpdate</code>&nbsp;之前拆分。而订阅的逻辑也在&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentDidUpdate</code>&nbsp;之间传播。<code>componentDidMount</code>&nbsp;包含两个任务的代码。</p>
<p>使用 Hook 解决这个问题其实就像之前使用&nbsp;<code>useState</code>&nbsp;解决问题一样,可以使用多个 effect,然后将不相关的逻辑都拆分成不同的 effect。</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">function <span class="token function">FriendStatusWithCounter<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token number">0<span class="token punctuation">)<span class="token punctuation">;
<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;

<span class="token keyword">const <span class="token punctuation"> <span class="token operator">= <span class="token function">useState<span class="token punctuation">(<span class="token keyword">null<span class="token punctuation">)<span class="token punctuation">;
<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">return <span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
      ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;

<span class="token keyword">function <span class="token function">handleStatusChange<span class="token punctuation">(<span class="token parameter">status<span class="token punctuation">) <span class="token punctuation">{
    <span class="token function">setIsOnline<span class="token punctuation">(status<span class="token punctuation">.isOnline<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token comment">// ...
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>Hooks 允许我们根据它正在做的事情而不是生命周期方法名称来拆分代码。React 将按照指定的顺序应用组件使用的每个 effect。</p>
<h3>2、说明:为什么 effect 在每次 update 都会运行</h3>
<p>如果你习惯了使用 class 组件,你可能想知道为什么每次 re-render 之后,effect 的清理都会执行,而不是在卸载过程中只执行一次(打断点就能知道)。</p>
<p>在&nbsp;<code>useState</code>&nbsp;的文章(http://www.ptbird.cn/react-hook-use-state-hook.html) 中有一个&nbsp;<code>FriendStatus</code>&nbsp;来表示好友是否在线,class 组件冲 this.props 中读取&nbsp;<code>friend.id</code>,在组件 mount 之后,就订阅朋友的状态,并在卸载期间取消订阅:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">componentDidMount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentWillUnmount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>但是如果&nbsp;<code>friend</code>&nbsp;prop 在组件出现在屏幕上时发生了和变化,又会发生什么呢?类组件将继续显示不同的朋友的在线状态,这是一个bug,并且因为取消订阅使用了错误的 friend ID,卸载时还可能导致内存泄露或崩溃。</p>
<p>因此在类组件中,需要添加&nbsp;<code>componentDidUpdate</code>&nbsp;来处理这种情况:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">componentDidMount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token parameter">prevProps<span class="token punctuation">) <span class="token punctuation">{
    <span class="token comment">// Unsubscribe from the previous friend.id
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(
      prevProps<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
    <span class="token comment">// Subscribe to the next friend.id
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentWillUnmount<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(
      <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">,
      <span class="token keyword">this<span class="token punctuation">.handleStatusChange
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>忘记处理&nbsp;<code>componentDidUpdate</code>&nbsp;是 React 应用程序中常见的错误。</p>
<p>如果使用 Hook :</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token keyword">function <span class="token function">FriendStatus<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// ...
<span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">return <span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
      ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
    <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>上面的代码如果&nbsp;<code>this.props.friend</code>&nbsp;发生了变化,也不会受到影响。</p>
<p>没有用于处理 update 的特殊的代码,因为默认情况下&nbsp;<code>useEffect</code>&nbsp;会处理它们。它们在应用下一个 effect 之前清楚之前的 effect。为了说明这一点,下面是一个订阅和取消订阅调用的序列,这个组件可能随着时间的推移产生:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token comment">// Mount with { friend: { id: 100 } } props
ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(<span class="token number">100<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;   <span class="token comment">// Run first effect

<span class="token comment">// Update with { friend: { id: 200 } } props
ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(<span class="token number">100<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// Clean up previous effect
ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(<span class="token number">200<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;   <span class="token comment">// Run next effect

<span class="token comment">// Update with { friend: { id: 300 } } props
ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(<span class="token number">200<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// Clean up previous effect
ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(<span class="token number">300<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;   <span class="token comment">// Run next effect

<span class="token comment">// Unmount
ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(<span class="token number">300<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// Clean up last effect</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这种默认行为确保了一致性,并防止由于缺少 update 的处理逻辑而产生 class 组件中常见的错误。</p>
<h3>3、Tip:跳过 effect 优化性能</h3>
<p>在某些情况下,每次 render 后清理或者使用 effect 可能会产生性能问题。在类组件中,可以通过&nbsp;<code>componentDidUpdate</code>&nbsp;中编写&nbsp;<code>prevProps</code>&nbsp;或&nbsp;<code>prevState</code>&nbsp;的额外比较来解决这个问题:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token parameter">prevProps<span class="token punctuation">, prevState<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(prevState<span class="token punctuation">.count <span class="token operator">!== <span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token punctuation">) <span class="token punctuation">{
    document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这个要求很常见,而这种方式已经被内置到&nbsp;<code>useEffect</code>&nbsp;Hook 的 API中,如果在重新渲染之间没有更新某些值,则可以告诉 React 跳过 effect,为了实现这种方式,需要将数组作为可选的第二个参数传递给&nbsp;<code>useEffect</code>:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
document<span class="token punctuation">.title <span class="token operator">= <span class="token template-string"><span class="token string">`You clicked <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${count<span class="token interpolation-punctuation punctuation">}<span class="token string"> times`<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">, <span class="token punctuation"><span class="token punctuation">)<span class="token punctuation">;<span class="token comment">// 只有在 count 发生变化的时候才会执行这个 effect</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>上面的例子中,&nbsp;<code></code>&nbsp;作为第二个参数传递。如果&nbsp;<code>count = 5</code>,然后组件如果进行了 re-render,如果&nbsp;<code>count=5</code>,则 React 会比较前一个 render 和 下一个 render 的值。因为两次&nbsp;<code>5 === 5</code>,因此React 会跳过这次 effect,这是性能优化。</p>
<p>当&nbsp;<code>count = 6</code>&nbsp;的时候,React 会比较&nbsp;<code>5 !== 6</code>。此时,React 会重新去调用 effect,如果数组中有多个项目,只要有一个的比较值是不相同的, React 也会执行这个 effect。</p>
<p>上面的作用,也同样应用于 cleanup 的 effect:</p>
<pre class="line-numberslanguage-javascript"><code class="language-javascript"><span class="token function">useEffect<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
ChatAPI<span class="token punctuation">.<span class="token function">subscribeToFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">return <span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=&gt; <span class="token punctuation">{
    ChatAPI<span class="token punctuation">.<span class="token function">unsubscribeFromFriendStatus<span class="token punctuation">(props<span class="token punctuation">.friend<span class="token punctuation">.id<span class="token punctuation">, handleStatusChange<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">, <span class="token punctuation"><span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// 只有 props.friend.id 变化的时候才会调用 effect</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>未来,第二个参数可能会通过构建的时候,转换自动添加。</p>
<h4>注意</h4>
<p>如果使用此优化,需要确保数组包含外部作用域随时间变化且 effect 使用的任何值。否则,你的代码将引用之前渲染的旧值。在&nbsp;https://reactjs.org/docs/hooks-reference.html&nbsp;有又关于 Hooks 优化的更多内容。</p>
<p>如果要运行效果并且仅将其清理一次(在 mount 和 unmount 的时候),可以把空数组&nbsp;<code>[]</code>&nbsp;作为第二个参数传递。这告诉React你的效果不依赖于来自props或state的任何值,所以它永远不需要重新运行。这不会作为特殊情况进行处理 - 它直接遵循输入数组的工作方式。虽然传递&nbsp;<code>[]</code>&nbsp;更接近&nbsp;<code>componentDidMount</code>&nbsp;和&nbsp;<code>componentWillUnmount</code>&nbsp;的模式,但是不建议将其作为一种习惯,如果存在订阅的话,经常会导致错误。</p>
<p>不要忘记 React 会延迟运行&nbsp;<code>useEffect</code>&nbsp;直到浏览器 render 之后,所以进行额外的操作也不是问题。</p>
<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><ins class="adsbygoogle" data-ad-client="ca-pub-6086496827186858" data-ad-slot="7423034880" data-ad-format="auto" data-full-width-responsive="true" data-adsbygoogle-status="done" data-overlap-observer-io="false"><ins id="aswift_1_expand"><ins id="aswift_1_anchor"><iframe id="aswift_1" name="aswift_1" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="803" height="90"></iframe></ins></ins></ins></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<ul id="nav-footer">
<li class="item1"></li>
<li>文章已经结束啦</li>
</ul>
</div>
<div>
<div class="post-tags"><span style="color: rgba(0, 133, 115, 1)">标签:</span>reacthookuseEffect</div>
</div>
<div>
<p>文章版权:Postbird-There I am , in the world more exciting!</p>
<p>本文链接:http://www.ptbird.cn/react-hoot-useEffect.html</p>
<p>转载请注明文章原始出处 !</p>
</div>
<div>
<div id="qrcodeContent">&nbsp;</div>
</div><br><br>
来源:https://www.cnblogs.com/FineDay/p/11264301.html
頁: [1]
查看完整版本: React Hook:使用 useEffect