{"componentChunkName":"component---src-templates-blog-post-js","path":"/zh-hans/making-setinterval-declarative-with-react-hooks/","result":{"data":{"site":{"siteMetadata":{"title":"Ibrahim personal blog","author":"Ibrahim"}},"markdownRemark":{"id":"9ad8c1fc-7c66-56c9-9bbd-c4509f2a9eb5","html":"<p>如果你玩了几小时的 <a href=\"https://reactjs.org/docs/hooks-intro.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">React Hooks</a>，你可能会陷入一个烦人的问题：在用 <code class=\"language-text\">setInterval</code> 时总会<a href=\"https://stackoverflow.com/questions/53024496/state-not-updating-when-using-react-state-hook-within-setinterval\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">偏离</a>自己想要的效果。</p>\n<p>这是 Ryan Florence 的<a href=\"https://mobile.twitter.com/ryanflorence/status/1088606583637061634\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">原话</a>：</p>\n<blockquote>\n<p>我已经碰到许多人提到带有 setInterval 的 hooks 时常会打 React 的脸，但因为 stale state 引发的问题我还是头一次见。 如果在 hooks 中这个问题极其困难，那么相比于 class component，我们遇到了不同级别复杂度的问题。</p>\n</blockquote>\n<p>老实说，我觉得这些人是有一套的，至少为此困惑了。</p>\n<p>然而我发现这不是 Hooks 的问题，而是 <a href=\"/react-as-a-ui-runtime/\">React编程模型</a> 和 <code class=\"language-text\">setInterval</code> 不匹配造成的。Hooks 比 class 更贴近 React 编程模型，使这种不匹配更明显。</p>\n<p>在这篇文章里，我们会看到 intervals 和 Hooks 是如何玩在一起的、为什么这个方案有意义和可以提供哪些新的功能。</p>\n<hr>\n<p><strong>免责声明：这篇文章的重点是一个 <em>问题样例</em>。即使 API 可以简化上百种情况，议论始终指向更难的问题上</strong>。</p>\n<p>如果你刚入手 Hooks 且不知道这儿在说什么，先查看 <a href=\"https://juejin.im/post/5be98a87f265da616e4bf8a4\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">这个介绍</a> 和 <a href=\"https://reactjs.org/docs/hooks-intro.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">文档</a>。这篇文章假设你已经使用 Hooks 超过一个小时。</p>\n<hr>\n<h2 id=\"直接给我看代码\"><a href=\"#%E7%9B%B4%E6%8E%A5%E7%BB%99%E6%88%91%E7%9C%8B%E4%BB%A3%E7%A0%81\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>直接给我看代码</h2>\n<p>不用多说，这是一个每秒递增的计数器：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useState<span class=\"token punctuation\">,</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token comment\">// 你自己的代码</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（这是 <a href=\"https://codesandbox.io/s/105x531vkq\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>）。</em></p>\n<p>demo里面的 <code class=\"language-text\">useInterval</code> 不是一个内置 React Hook，而是一个我写的 <a href=\"https://reactjs.org/docs/hooks-custom.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">custom Hook</a>。</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useState<span class=\"token punctuation\">,</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">callback<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> savedCallback <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 保存新回调</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    savedCallback<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> callback<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 建立 interval</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delay <span class=\"token operator\">!==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>(这是前面的demo中，你可能错过的 <a href=\"https://codesandbox.io/s/105x531vkq\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>。)</em></p>\n<p><strong>我的 <code class=\"language-text\">useInterval</code> Hook 内置了一个 interval 并在 unmounting 的时候清除</strong>，它是一个作用在组件生命周期里的 <code class=\"language-text\">setInterval</code> 和 <code class=\"language-text\">clearInterval</code> 的组合。</p>\n<p>你可以随意将它复制粘贴到项目中或者用 npm 导入。</p>\n<p><strong>如果你不在乎它是怎么实现的，你可以停止阅读了！接下来的部分是给想深度挖掘 React Hooks 的乡亲们准备的</strong>。</p>\n<hr>\n<h2 id=\"等什么-\"><a href=\"#%E7%AD%89%E4%BB%80%E4%B9%88-\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>等什么?! 🤔</h2>\n<p>我知道你在想什么：</p>\n<blockquote>\n<p>Dan，这段代码根本没什么意思，「单单是 JavaScript」能有什么？承认 React 用 Hooks 钓到了 「鲨鱼」 吧！</p>\n</blockquote>\n<p><strong>一开始我也是这样想的，但后来我改变想法了，我也要改变你的</strong>。在解释这段代码为什么有意义之前，我想展示下它能做什么。</p>\n<hr>\n<h2 id=\"为什么-useinterval-是更好的api\"><a href=\"#%E4%B8%BA%E4%BB%80%E4%B9%88-useinterval-%E6%98%AF%E6%9B%B4%E5%A5%BD%E7%9A%84api\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>为什么 <code class=\"language-text\">useInterval()</code> 是更好的API</h2>\n<p>提醒你下，我的 <code class=\"language-text\">useInterval</code> Hook 接收 一个 function 和 一个 delay 参数：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>这样看起很像 <code class=\"language-text\">setInterval</code>：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><strong>所以为什么不直接用 <code class=\"language-text\">setInterval</code> 呢</strong>？</p>\n<p>一开始可能不明显，但你发现我的 <code class=\"language-text\">useInterval</code> 与 <code class=\"language-text\">setInterval</code> 之间的不同后，你会看出 <strong>它的参数是「动态地」</strong>。</p>\n<p>我将用具体的例子来说明这一点。</p>\n<hr>\n<p>假设我们希望 delay 可调：</p>\n<p><img src=\"/35e4f35a8585255b11c090aed9f72433/counter_delay.gif\" alt=\"Counter with an input that adjusts the interval delay\"></p>\n<p>虽然你不一定要用到输入控制 delay，但动态调整可能很有用 —— 例如，用户切换到其他选项卡时，要减少 AJAX 轮询更新间隔。</p>\n<p>所以在 class 里你要怎么用 <code class=\"language-text\">setInterval</code> 做到这一点呢？我会这么做：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Counter</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span>\n  state <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    count<span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n    delay<span class=\"token operator\">:</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">componentDidMount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>interval <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>tick<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span></span><span class=\"gatsby-highlight-code-line\"></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">componentDidUpdate</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevProps<span class=\"token punctuation\">,</span> prevState</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>prevState<span class=\"token punctuation\">.</span>delay <span class=\"token operator\">!==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>delay<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">      <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>interval<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>interval <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>tick<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token punctuation\">}</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span></span><span class=\"gatsby-highlight-code-line\"></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">componentWillUnmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>interval<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span></span><span class=\"gatsby-highlight-code-line\"></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token function-variable function\">tick</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">      count<span class=\"token operator\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span></span>\n  <span class=\"token function-variable function\">handleDelayChange</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> delay<span class=\"token operator\">:</span> <span class=\"token function\">Number</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>delay<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleDelayChange<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（这是 <a href=\"https://codesandbox.io/s/mz20m600mp\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>。）</em></p>\n<p>这样也不错！</p>\n<p>Hook 版本看起来是什么样子的？</p>\n<p><font size=\"50\">🥁🥁🥁</font></p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">,</span> setDelay<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token comment\">// 这是你的自定义逻辑</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">handleDelayChange</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setDelay</span><span class=\"token punctuation\">(</span><span class=\"token function\">Number</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>delay<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleDelayChange<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（这是 <a href=\"https://codesandbox.io/s/mz20m600mp\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>。）</em></p>\n<p>是的，<em>这就是全部了</em>。</p>\n<p>不像 class 的版本，<code class=\"language-text\">useInterval</code> Hook 例子中，「更新」成动态调整 delay 很简单：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token comment\">// 固定 delay</span>\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token comment\">// 可调整 delay</span>\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>当 <code class=\"language-text\">useInterval</code> Hook 接收到不同 delay，它会重设 interval。</p>\n<p><strong>声明一个带有动态调整 delay 的 interval，来替代写 <em>添加</em>和<em>清除</em> interval 的代码 —— <code class=\"language-text\">useInterval</code> Hook 帮我们做到了</strong>。</p>\n<p>如果我想暂时 <em>暂停</em> interval 要怎么做？我可以用一个 state 来做到：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">,</span> setDelay<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>isRunning<span class=\"token punctuation\">,</span> setIsRunning<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> isRunning <span class=\"token operator\">?</span> delay <span class=\"token operator\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p><em>（这是 <a href=\"https://codesandbox.io/s/l240mp2pm7\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">demo</a>!）</em></p>\n<p>这让我对 React 和 Hooks 再次感到兴奋。我们可以包装现有的命令式 APIs 和创建更贴近表达我们意图的声明式 APIs。就拿渲染来说，我们可以同时准确地描述每个时间点过程，而不用小心地用指令来操作它。</p>\n<hr>\n<p>我希望到这里你们开始觉得 <code class=\"language-text\">useInterval()</code> Hook 是一个更好的 API 了 —— 至少和组件比。</p>\n<p><strong>但为什么在 Hooks 中使用 <code class=\"language-text\">setInterval()</code> 和 <code class=\"language-text\">clearInterval()</code> 让人心烦呢</strong>？让我们回到计数器例子并试着手动实现它。</p>\n<hr>\n<h2 id=\"第一次尝试\"><a href=\"#%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%B0%9D%E8%AF%95\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>第一次尝试</h2>\n<p>我会从一个只渲染初始状态的简单例子开始：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>现在我想要一个每秒增加的 interval，它是一个<a href=\"https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">需要清理副作用</a>的，所以我将用到 <code class=\"language-text\">useEffect()</code> 并返回清理函数：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">      <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（查看 <a href=\"https://codesandbox.io/s/7wlxk1k87j\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>.）</em></p>\n<p>这种工作看起来很简单对吧？</p>\n<p><strong>但是，这代码有一个奇怪的行为</strong>。</p>\n<p>默认情况下，React 会在每次渲染后重执行 effects，这是有目的的，这有助于避免 React class 组件的<a href=\"https://reactjs.org/docs/hooks-effect.html#explanation-why-effects-run-on-each-update\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">某种 bugs</a>。</p>\n<p>这通常是好的，因为需要许多订阅 API 可以随时顺手移除老的监听者和加个新的。但是，<code class=\"language-text\">setInterval</code> 和它们不一样。当我们执行 <code class=\"language-text\">clearInterval</code> 和 <code class=\"language-text\">setInterval</code> 时，它们会进入时间队列里，如果我们频繁重渲染和重执行 effects，interval 有可能没有机会被执行！</p>\n<p>我们可以通过以<em>更短</em>间隔重渲染我们的组件，来发现这个 bug：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 重渲染和重执行 Counter 的 effects</span>\n  <span class=\"token comment\">// 这里会发生 clearInterval()</span>\n  <span class=\"token comment\">// 在 interval 被执行前 setInterval()</span>\n  ReactDOM<span class=\"token punctuation\">.</span><span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Counter</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">,</span> rootElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><em>（看这个 bug 的 <a href=\"https://codesandbox.io/s/9j86r218y4\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">demo</a>）</em></p>\n<hr>\n<h2 id=\"第二次尝试\"><a href=\"#%E7%AC%AC%E4%BA%8C%E6%AC%A1%E5%B0%9D%E8%AF%95\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>第二次尝试</h2>\n<p>你可能知道 <code class=\"language-text\">useEffect()</code> 允许我们<a href=\"https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><em>选择性地</em></a>进行重执行 effects，你可以设定一个依赖数组作为第二个参数，React 只会在数组里的某个发生变化时重运行：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  document<span class=\"token punctuation\">.</span>title <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">You clicked </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>count<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> times</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\"><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>当我们 <em>只</em> 想在 mount 时执行 effect 和 unmount 时清理它，我们可以传空 <code class=\"language-text\">[]</code> 的依赖数组。</p>\n<p>但是，如果你不熟悉 JavaScript 的闭包，会碰到一个常见的错误。我们现在就来制造这个错误！（我们还建立了一个尽早反馈这个错误的 <a href=\"https://github.com/facebook/react/pull/14636\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">lint 规则</a>，但还没准备好。）</p>\n<p>在第一次尝试中，我们的问题是重运行 effects 时使得 timer 过早被清除，我们可以尝试不重运行去修复它们：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>但是，现在我们的计时器更新到 1 就不动了。（<a href=\"https://codesandbox.io/s/jj0mk6y683\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">查看真实 bug</a>。）</p>\n<p>发生了什么？！</p>\n<p><strong>问题在于，<code class=\"language-text\">useEffect</code> 在第一次渲染时获取值为 0 的 <code class=\"language-text\">count</code></strong>，我们不再重执行 effect，所以 <code class=\"language-text\">setInterval</code> 一直引用第一次渲染时的闭包 <code class=\"language-text\">count</code>，以至于 <code class=\"language-text\">count + 1</code> 一直是 <code class=\"language-text\">1</code>。哎呀呀！</p>\n<p><strong>我可以听见你咬牙切齿了，Hooks 真烦人对吧</strong>？</p>\n<p>修复它的<a href=\"https://codesandbox.io/s/j379jxrzjy\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">一种方法</a>是用像 <code class=\"language-text\">setCount(c =&gt; c + 1)</code> 这样的 「updater」替换 <code class=\"language-text\">setCount(count + 1)</code>，这样可以读到新 state 变量。但这个无法帮助你获取到新的 props。</p>\n<p><a href=\"https://codesandbox.io/s/00o9o95jyv\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">另一个方法</a>是用 <a href=\"https://reactjs.org/docs/hooks-reference.html#usereducer\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">useReducer()</code></a>。这种方法为你提供了更大的灵活性。在 reducer 中，你可以访问到当前 state 和新的 props。<code class=\"language-text\">dispatch</code> 方法本身永远不会改变，所以你可以从任何闭包中将数据放入其中。<code class=\"language-text\">useReducer()</code> 有个约束是你不可以用它执行副作用。（但是，你可以返回新状态 —— 触发一些 effect。）</p>\n<p><strong>但为什么要变得这么复杂</strong>？</p>\n<hr>\n<h2 id=\"阻抗不匹配\"><a href=\"#%E9%98%BB%E6%8A%97%E4%B8%8D%E5%8C%B9%E9%85%8D\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>阻抗不匹配</h2>\n<p>这个术语有时会被提到，<a href=\"https://haacked.com/archive/2004/06/15/impedance-mismatch.aspx/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Phil Haack</a> 解释如下：</p>\n<blockquote>\n<p>有人说数据库来自火星而对象来自金星，数据库不会自然地映射到对象模型。这很像试图将磁铁的两极推到一起。</p>\n</blockquote>\n<p>我们的「阻抗匹配」不在数据库和对象之间，它在 React 编程模型和命令式 <code class=\"language-text\">setInterval</code> API 之间。</p>\n<p><strong>一个 React 组件可能在 mounted 之前流经许多不同的 state，但它的渲染结果将<em>一次性全部</em>描述出来</strong>。</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token comment\">// 描述每次渲染</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span></code></pre></div>\n<p>Hooks 使我们把相同的声明方法用在 effects 上：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token comment\">// 描述每个间隔状态</span>\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> isRunning <span class=\"token operator\">?</span> delay <span class=\"token operator\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>我们不<em>设置</em> interval，但指定它<em>是否</em>设置延迟或延迟多少，我们的 Hooks 做到了，用离散术语描述连续过程</p>\n<p><strong>相反，<code class=\"language-text\">setInterval</code> 没有及时地描述过程 —— 一旦设定了 interval，除了清除它，你无法对它做任何改变</strong>。</p>\n<p>这就是 React 模型和 <code class=\"language-text\">setInterval</code> API 之间的不匹配。</p>\n<hr>\n<p>React 组件中的 props 和 state 是可以改变的， React 会重渲染它们且「丢弃」任何关于上一次渲染的结果，它们之间不再有相关性。</p>\n<p><code class=\"language-text\">useEffect()</code> Hook 也「丢弃」上一次渲染结果，它会清除上一次 effect 再建立下一个 effect，下一个 effect 锁住新的 props 和 state，这也是我们<a href=\"https://codesandbox.io/s/7wlxk1k87j\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">第一次尝试</a>简单示例可以正确工作的原因。</p>\n<p><strong>但 <code class=\"language-text\">setInterval</code> 不会「丢弃」。</strong> 它会一直引用老的 props 和 state 直到你把它换掉 —— 不重置时间你是无法做到的。</p>\n<p>或者等等，你可以做到？</p>\n<hr>\n<h2 id=\"refs-可以做到！\"><a href=\"#refs-%E5%8F%AF%E4%BB%A5%E5%81%9A%E5%88%B0%EF%BC%81\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Refs 可以做到！</h2>\n<p>这个问题归结为下面这样：</p>\n<ul>\n<li>我们在第一次渲染时执行带 <code class=\"language-text\">callback1</code> 的 <code class=\"language-text\">setInterval(callback1, delay)</code>。</li>\n<li>我们在下一次渲染时得到携带新的 props 和 state 的 <code class=\"language-text\">callbaxk2</code>。</li>\n<li>我们无法在不重置时间的情况下替换掉已经存在的 interval。</li>\n</ul>\n<p><strong>那么如果我们根本不替换 interval，而是引入一个指向<em>新</em> interval 回调的可变 <code class=\"language-text\">savedCallback</code> 会怎么样</strong>？</p>\n<p>现在我们来看看这个方案：</p>\n<ul>\n<li>我们调用 <code class=\"language-text\">setInterval(fn, delay)</code>，其中 <code class=\"language-text\">fn</code> 调用 <code class=\"language-text\">savedCallback</code>。</li>\n<li>第一次渲染后将 <code class=\"language-text\">savedCallback</code> 设为 <code class=\"language-text\">callback1</code>。</li>\n<li>下一次渲染后将 <code class=\"language-text\">savedCallback</code> 设为 <code class=\"language-text\">callback2</code>。</li>\n<li>???</li>\n<li>完成</li>\n</ul>\n<p>这个可变的 <code class=\"language-text\">savedCallback</code> 需要在重新渲染时「可持续（persist）」，所以不可以是一个常规变量，我们想要一个类似实例的字段。</p>\n<p><a href=\"https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">正如我们从 Hooks FAQ 中学到的</a>，<code class=\"language-text\">useRef()</code> 给出了我们想要的结果：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token keyword\">const</span> savedCallback <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// { current: null }</span></code></pre></div>\n<p><em>（你可能熟悉 React 中的 <a href=\"https://reactjs.org/docs/refs-and-the-dom.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">DOM refs</a>）。Hooks 使用相同的概念来保存任意可变值。ref 就像一个「盒子」，你可以放任何东西</em></p>\n<p><code class=\"language-text\">useRef()</code> 返回一个有带有 <code class=\"language-text\">current</code> 可变属性的普通对象在 renders 间共享，我们可以保存<em>新</em>的 interval 回掉给它：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token keyword\">function</span> <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 可以读到新 props，state等。</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 每次渲染后，保存新的回调到我们的 ref 里。</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"gatsby-highlight-code-line\">    savedCallback<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> callback<span class=\"token punctuation\">;</span></span>  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>之后我们便可以从我们的 interval 中读取和调用它：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"gatsby-highlight-code-line\">      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>感谢 <code class=\"language-text\">[]</code>，不重执行我们的 effect，interval 就不会被重置。同时，感谢 <code class=\"language-text\">savedCallback</code> ref，让我们可以一直在新渲染之后读取到回调，并在 interval tick 里调用它。</p>\n<p>这是完整的解决方案：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> savedCallback <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">function</span> <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"gatsby-highlight-code-line\">    savedCallback<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> callback<span class=\"token punctuation\">;</span></span>  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"gatsby-highlight-code-line\">      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（看 <a href=\"https://codesandbox.io/s/3499qqr565\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>。）</em></p>\n<hr>\n<h2 id=\"提取一个-hook\"><a href=\"#%E6%8F%90%E5%8F%96%E4%B8%80%E4%B8%AA-hook\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>提取一个 Hook</h2>\n<p>不可否认，上面的代码令人困惑，混合相反的范式令人费解，还可能弄乱可变 refs。</p>\n<p><strong>我觉得 Hooks 提供了比 class 更低级的原语 —— 但它们的美丽在于它们使我们能够创作并创造出更好的陈述性抽象</strong>。</p>\n<p>理想情况下，我只想这样写：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>我将我 ref 机制的代码复制粘贴到一个 custom Hook：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">callback</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> savedCallback <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    savedCallback<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> callback<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>当前，<code class=\"language-text\">1000</code> delay 是写死的，我想把它变成一个参数：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">callback<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></code></pre></div>\n<p>我会在创建好 interval 后使用它：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p> 现在 <code class=\"language-text\">delay</code> 可以在 renders 之间改变，我需要在我的 interval effect 依赖部分声明它：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>等等，我们不是要避免重置 interval effect，并专门通过 <code class=\"language-text\">[]</code> 来避免它吗？不完全是，我们只想在<em>回调</em>改变时避免重置它，但当 <code class=\"language-text\">delay</code> 改变时，我们<em>想要</em>重启 timer！</p>\n<p>让我们检查下我们的代码是否有效：</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">callback<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> savedCallback <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    savedCallback<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> callback<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（尝试它 <a href=\"https://codesandbox.io/s/xvyl15375w\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox</a>。）</em></p>\n<p>有效！我们现在可以不用想太多 <code class=\"language-text\">useInterval()</code> 的实现过程，在任意组件中使用它。</p>\n<h2 id=\"福利：暂停-interval\"><a href=\"#%E7%A6%8F%E5%88%A9%EF%BC%9A%E6%9A%82%E5%81%9C-interval\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>福利：暂停 Interval</h2>\n<p>假设我们希望能够通过传递 <code class=\"language-text\">null</code> 作为 <code class=\"language-text\">delay</code> 来暂停我们的 interval：</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">,</span> setDelay<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>isRunning<span class=\"token punctuation\">,</span> setIsRunning<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> isRunning <span class=\"token operator\">?</span> delay <span class=\"token operator\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span></code></pre></div>\n<p>如何实现这个？答案时：不创建 interval。</p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\">  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">tick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      savedCallback<span class=\"token punctuation\">.</span><span class=\"token function\">current</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n<span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delay <span class=\"token operator\">!==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span>      <span class=\"token keyword\">let</span> id <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span>tick<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><em>（看 <a href=\"https://codesandbox.io/s/l240mp2pm7\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>。）</em></p>\n<p>就是这样。此代码处理了所有可能的变化：改变 delay、暂停、或者恢复 interval。<code class=\"language-text\">useEffect()</code> API 要求我们花费更多的前期工作来描述建立和清除 —— 但添加新案例很容易。</p>\n<h2 id=\"福利：有趣的-demo\"><a href=\"#%E7%A6%8F%E5%88%A9%EF%BC%9A%E6%9C%89%E8%B6%A3%E7%9A%84-demo\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>福利：有趣的 Demo</h2>\n<p><code class=\"language-text\">useInterval()</code> Hook 真的很好玩，当副作用是陈述性的，将复杂的行为编排在一起要容易得多。</p>\n<p><strong>例如：我们 interval 中 <code class=\"language-text\">delay</code> 可以受控于另外一个：</strong></p>\n<p><img src=\"/10cfc4b38497a46980d3a13048a56e36/counter_inception.gif\" alt=\"Counter that automatically speeds up\"> <h1>!!!!</h1></p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>delay<span class=\"token punctuation\">,</span> setDelay<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>count<span class=\"token punctuation\">,</span> setCount<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 增加计数器</span>\n  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setCount</span><span class=\"token punctuation\">(</span>count <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token comment\">// 每秒加速</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token function\">useInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delay <span class=\"token operator\">></span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span></span><span class=\"gatsby-highlight-code-line\">      <span class=\"token function\">setDelay</span><span class=\"token punctuation\">(</span>delay <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span><span class=\"gatsby-highlight-code-line\">    <span class=\"token punctuation\">}</span></span><span class=\"gatsby-highlight-code-line\">  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">handleReset</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setDelay</span><span class=\"token punctuation\">(</span><span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Counter: </span><span class=\"token punctuation\">{</span>count<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h4</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Delay: </span><span class=\"token punctuation\">{</span>delay<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h4</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleReset<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">        Reset delay</span>\n<span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><em>（看 <a href=\"https://codesandbox.io/s/znr418qp13\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CodeSandbox demo</a>！）</em></p>\n<h2 id=\"尾声总结\"><a href=\"#%E5%B0%BE%E5%A3%B0%E6%80%BB%E7%BB%93\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>尾声总结</h2>\n<p>Hooks 需要花时间去习惯 —— <em>特别</em>是在跨越命令式和声明式的代码上。你可以创建像 <a href=\"http://react-spring.surge.sh/hooks\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">React Spring</a> 一样的抽象，但有时它们会让你不安。</p>\n<p>Hooks 还处于前期阶段，无疑此模式仍需要修炼和比较。如果你习惯跟随众所周知的「最佳实践」，不要急于采用 Hooks，它需要很多的尝试和探索。</p>\n<p>我希望这篇文章可以帮助你理解带有 <code class=\"language-text\">setInterval()</code> 等 API 的 Hooks 的相关常见问题、可以帮助你克服它们的模式、及享用建立在它们之上更具表达力的声明式 APIs 的甜蜜果实。</p>","timeToRead":17,"frontmatter":{"title":"使用 React Hooks 声明 setInterval","date":"February 04, 2019","spoiler":"我是如何学习停止担心和喜爱 refs 的。","tags":null},"fields":{"slug":"/zh-hans/making-setinterval-declarative-with-react-hooks/","langKey":"zh-hans"}}},"pageContext":{"slug":"/zh-hans/making-setinterval-declarative-with-react-hooks/","translations":["fr","zh-hans"],"translatedLinks":[]}}}