{"componentChunkName":"component---src-templates-blog-post-js","path":"/react-trap/","result":{"data":{"site":{"siteMetadata":{"title":"😏","author":"thoamsy"}},"markdownRemark":{"id":"d12f42c1-ac7e-5e5c-9c30-fe4f45d5e7a6","html":"<p>收集一些不为人所知的，React 的细节问题。帮助自己理顺 React 内部深层的逻辑。</p>\n<h2 id=\"synthetic-event-和-dom-event-同时出现的问题\" style=\"position:relative;\"><a href=\"#synthetic-event-%E5%92%8C-dom-event-%E5%90%8C%E6%97%B6%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98\" aria-label=\"synthetic event 和 dom event 同时出现的问题 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>Synthetic Event 和 DOM Event 同时出现的问题</h2>\n<p>首先解释下，为什么 React 会有 Synthetic Event 这个概念。主要两个原因</p>\n<ol>\n<li>13 年的时候，浏览器发展比如今天，不同的浏览器在同一个概念上，可能会有不同的 API，另外那个时候还需要兼容 IE8。</li>\n<li>针对大量创建 Event Object 的场景，有一个优化。这就诞生了 Event Pooling 这个概念。换句话说，在 React 中不需要之前的<strong>事件委托</strong></li>\n</ol>\n<p>而 React 为了实现 Synthetic Event，是通过将所有的 <code class=\"language-text\">onXXX</code> 事件注册在 <code class=\"language-text\">document</code> 上来实现的。所以这就在某些十分特殊的场景下，会产生一些问题。看下面代码，或者 <a href=\"https://codesandbox.io/s/v1m3o069j7\">sandbox</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> useCallback<span class=\"token punctuation\">,</span> useEffect <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\">const</span> <span class=\"token function-variable function\">App</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>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleClickOnDocument</span> <span class=\"token operator\">=</span> <span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on document'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleClickOnWindow</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>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on window'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</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    document<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnDocument<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    window<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnWindow<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 punctuation\">{</span>\n      document<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnDocument<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      window<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnWindow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <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 keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</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><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on div'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n        <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><span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n          e<span class=\"token punctuation\">.</span><span class=\"token function\">stopPropagation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span>\n      <span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        Click Me\n      </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\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> App<span class=\"token punctuation\">;</span></code></pre></div>\n<p>如果点击那个 button 会发生什么？在传统的 DOM 代码中，很明显什么都不会输出。但是在 React 中，我们会得到</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">handling event on the document</code></pre></div>\n<p>一起来分析下原因</p>\n<h3 id=\"原因\" style=\"position:relative;\"><a href=\"#%E5%8E%9F%E5%9B%A0\" aria-label=\"原因 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>原因</h3>\n<p>首先，在 button 被点击后，<code class=\"language-text\">stopPropagation</code> 起作用，使父组件 div 的 <em>onClick</em> Synthetic Event 不会被调用。另外，原生的 DOM 事件也会被触发。因为 <code class=\"language-text\">stopPropagation</code> 不同于 <code class=\"language-text\">stopImmediatePropagation</code>，所以 <code class=\"language-text\">document</code> 上的事件还是被调用。又因为 <em>window</em> 是 <code class=\"language-text\">document</code> 的父级，所以就不会继续运行。</p>\n<p>原因已经很明显了，但是我们可以更进一步。如果在 div 上有一个 native DOM 事件，那么在<strong>点击 button 后，到底是 button 的 Synthetic 事件先运行还是 button 的 native 事件？</strong></p>\n<div class=\"gatsby-highlight has-highlighted-lines\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"gatsby-highlight-code-line\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useCallback<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></span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">App</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>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleClickOnDocument</span> <span class=\"token operator\">=</span> <span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on document'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleClickOnWindow</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>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on window'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleClickDiv</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\">    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on div with ref'</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><span class=\"gatsby-highlight-code-line\">  <span class=\"token keyword\">const</span> div <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></span>  <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><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnDocument<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    window<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnWindow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">    div<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickDiv<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>    <span class=\"token keyword\">return</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><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnDocument<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      window<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickOnWindow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">      div<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleClickDiv<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></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 punctuation\">[</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=\"gatsby-highlight-code-line\">    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>div<span class=\"token punctuation\">}</span></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><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'handling event on div'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span></span><span class=\"token plain-text\">      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n        <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><span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"gatsby-highlight-code-line\">          console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'button'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></span>          e<span class=\"token punctuation\">.</span><span class=\"token function\">stopPropagation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span>\n      <span class=\"token punctuation\">></span></span><span class=\"token plain-text\"></span>\n<span class=\"token plain-text\">        Click Me</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>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> App<span class=\"token punctuation\">;</span></code></pre></div>\n<p>直觉上觉得是会打印出</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">button\nhandle event on div with ref\nhandling event on the document</code></pre></div>\n<p>但根据上面的说法， Synthetic Event 会绑定在 document 上，那么根据<strong>冒泡</strong>的顺序，div 的 native 事件会先运行。可以得出下面结论</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">handling event on div with ref\nbutton\nhandling event on document</code></pre></div>\n<h3 id=\"结论\" style=\"position:relative;\"><a href=\"#%E7%BB%93%E8%AE%BA\" aria-label=\"结论 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>结论</h3>\n<p>因为 Synthetic Event 会将事件绑定在 document 上，从而导致在和 native dom 事件有一定逻辑关联的时候，出现一些不符合直觉的问题。这种场景下，一定需要小心。</p>\n<h3 id=\"相关链接\" style=\"position:relative;\"><a href=\"#%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5\" aria-label=\"相关链接 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>相关链接</h3>\n<ul>\n<li><a href=\"https://github.com/facebook/react/issues/12518\">Event listener attached to document will still be called after calling event.stopPropagation() · Issue #12518 · facebook/react · GitHub</a></li>\n<li><a href=\"https://reactjs.org/docs/events.html\">SyntheticEvent – React</a></li>\n</ul>\n<hr>\n<h2 id=\"context-的更新，会更新--provider-️-consumer-之间所有的组件么\" style=\"position:relative;\"><a href=\"#context-%E7%9A%84%E6%9B%B4%E6%96%B0%EF%BC%8C%E4%BC%9A%E6%9B%B4%E6%96%B0--provider-%EF%B8%8F-consumer-%E4%B9%8B%E9%97%B4%E6%89%80%E6%9C%89%E7%9A%84%E7%BB%84%E4%BB%B6%E4%B9%88\" aria-label=\"context 的更新，会更新  provider ️ consumer 之间所有的组件么 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>Context 的更新，会更新 ✨ Provider ➡️ Consumer 之间所有的组件么</h2>\n<p>这个问题提出来的依据主要是，<code class=\"language-text\">setState</code> 在 React 中是会<strong>一定会带来更新的</strong>，除非设置了 <code class=\"language-text\">memo</code> 或者 <code class=\"language-text\">shouldComponentUpdate</code> 这些优化措施。换句话说，一般情况下，每次 Context 中 value 的更新，必定伴随着一个 setState 的过程。而这个过程必定导致含有 <code class=\"language-text\">&lt;Context.Provider&gt;</code> 的组件往下更新，也就让我们无法观察到这个过程的更进一步的细节。</p>\n<p>如果想要探究这个问题的话，需要三个组件，分别被称为 App, Foo, Bar。</p>\n<p>App 是父组件，它的 <code class=\"language-text\">render</code> 中就返回了包含 Provider 的代码。</p>\n<p>Foo 组件是这个 Context 的消费者，它通过 Provider 拿到对应的展示内容。</p>\n<p>Bar 则是两个组件中的中间层，它的作用就是承接 Foo 和 App。同时它的 <code class=\"language-text\">shouldComponentUpdate</code> 会设置为 <strong>false</strong>。</p>\n<p>借助 Hooks，有下面的实验 🧪 代码。</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> useContext<span class=\"token punctuation\">,</span> memo<span class=\"token punctuation\">,</span> useState<span class=\"token punctuation\">,</span> useCallback <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\">const</span> FooContext <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createContext</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">Foo</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>\n  <span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> <span class=\"token function\">useContext</span><span class=\"token punctuation\">(</span>FooContext<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token string\">'foo'</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>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">i am </span><span class=\"token punctuation\">{</span>foo<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\"><span class=\"token keyword\">const</span> Bar <span class=\"token operator\">=</span> <span class=\"token function\">memo</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>  console<span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token string\">'bar'</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><span class=\"token class-name\">Foo</span></span> <span class=\"token punctuation\">/></span></span><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 keyword\">const</span> <span class=\"token function-variable function\">App</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>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>value<span class=\"token punctuation\">,</span> setValue<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\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> addValue <span class=\"token operator\">=</span> <span class=\"token function\">useCallback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span>value <span class=\"token operator\">+</span> <span class=\"token number\">1</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 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\">This is a context test</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>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>addValue<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">click me!!!</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 class=\"token class-name\">FooContext.Provider</span></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>value<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 class=\"token class-name\">Bar</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 class=\"token class-name\">FooContext.Provider</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><span class=\"token punctuation\">;</span></code></pre></div>\n<p>界面很 简单\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 800px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/54994c1a9ee04a660dadf84f9d566906/65654/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 39.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/svg+xml,%3csvg%20xmlns=\\'http://www.w3.org/2000/svg\\'%20width=\\'400\\'%20height=\\'158\\'%3e%3cpath%20d=\\'M1%2029c0%201%201%202%203%202s3%201%203%209c0%207%200%208%202%208s2-1%202-8v-9h3c2%200%203%200%203-2s-1-2-8-2-8%200-8%202m19-1l-1%2010c0%2010%200%2010%202%2010%203%200%203%200%203-6%200-5%201-5%203-5%201%200%202%200%202%205%200%206%200%206%203%206%202%200%202%200%202-6%200-8-3-11-8-9-2%201-2%201-2-2s-3-5-4-3m192%203l-1%202c-2%200-2%203-1%203l2%206c0%205%201%206%204%206l3-1-1-2c-2%200-2-1-2-4%200-4%200-5%202-5l1-1-1-2-2-2-2-2-2%202m17-1v1l-1%202c-2%200-2%203-1%203l2%206c0%205%201%206%204%206l3-1-1-2c-2%200-2-1-2-4%200-4%200-5%202-5l1-1-1-2-2-2c0-2-4-3-4-1m42%201l-1%202-1%202%201%201%201%205%201%206c2%202%206%201%206%200l-1-2-1-4%201-5%201-1-1-2-1-2-3-2-2%202M47%2033c-4%203-3%207%202%209l4%202h-6l-3-1c-3%202%202%205%206%205%209%200%2010-7%202-9l-4-2h5c1%202%204%200%203-1l-6-4-3%201m29%200c-4%203-3%207%202%209l4%202h-6c-1-2-4%200-3%201%200%202%205%204%208%203%207-1%207-7%201-9-3%200-5-1-5-2h5c1%202%204%200%203-1l-6-4-3%201m23%200c-3%201-5%203-4%205l3-1c3-1%206-1%206%201l-3%201c-4%200-7%202-7%204%200%203%202%205%205%205h9v-6c0-8-3-11-9-9m22%200c-2%201-5%205-5%208s5%208%208%208%208-5%206-6h-3c-3%203-5%202-5-3%200-3%200-4%202-4l3%201c0%202%204%203%204%201%200-4-6-6-10-5m16%200c-4%202-5%209-2%2013%202%203%209%203%2011%200%205-6-1-16-9-13m20%201h-2l-2-1c-2%200-2%201-2%208%200%206%200%207%202%207s2-1%202-5c0-5%202-8%204-7s2%202%202%206c0%205%200%206%202%206%203%200%203-12%200-14l-3-2-3%202m25-1c-4%202-5%209-2%2013s12%203%2012-2h-3c-1%202-6%201-6%200-1-2%200-2%204-2%206%200%207-1%205-5s-6-6-10-4m60%200c-9%205-4%2018%205%2015%203-1%205-3%205-5l-5%201c-2%201-5%201-5-1l5-1c5%200%205%200%205-3%200-5-6-8-10-6m16%200c-4%203-3%207%202%209l4%202h-6l-3-1c-3%202%202%205%206%205%209%200%2010-7%202-9l-4-2h5c1%202%204%200%203-1l-6-4-3%201m-62%204l2%203-2%204-2%204h3l3-2%201-2%202%202%204%202h2l-2-4-3-3%202-4c2-3%202-4%200-4l-3%202-2%202-1-2-4-2h-2l2%204\\'%20fill=\\'%23d3d3d3\\'%20fill-rule=\\'evenodd\\'/%3e%3c/svg%3e'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n        <source\n          data-srcset=\"/static/54994c1a9ee04a660dadf84f9d566906/ba381/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.webp 200w,\n/static/54994c1a9ee04a660dadf84f9d566906/7f61c/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.webp 400w,\n/static/54994c1a9ee04a660dadf84f9d566906/d00b9/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.webp 800w,\n/static/54994c1a9ee04a660dadf84f9d566906/a8a2c/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.webp 872w\"\n          sizes=\"(max-width: 800px) 100vw, 800px\"\n          type=\"image/webp\"\n        />\n        <source\n          srcset=\"/static/54994c1a9ee04a660dadf84f9d566906/772e8/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png 200w,\n/static/54994c1a9ee04a660dadf84f9d566906/e17e5/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png 400w,\n/static/54994c1a9ee04a660dadf84f9d566906/5a190/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png 800w,\n/static/54994c1a9ee04a660dadf84f9d566906/65654/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png 872w\"\n          sizes=\"(max-width: 800px) 100vw, 800px\"\n          type=\"image/png\"\n        />\n        <img\n          class=\"gatsby-resp-image-image lazyload\"\n          data-src=\"/static/54994c1a9ee04a660dadf84f9d566906/5a190/FABCB7EA-DB45-4A07-B5D7-AFDB521F6219.png\"\n          alt=\"FABCB7EA DB45 4A07 B5D7 AFDB521F6219\"\n          title=\"FABCB7EA DB45 4A07 B5D7 AFDB521F6219\"\n          loading=\"lazy\"\n          style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        />\n      </picture>\n  </a>\n    </span>\n通过点击 <em>click me</em> 那个 button，就能看到 /i am / 的变化。同时注意 Foo 和 Bar 中都有一个 <code class=\"language-text\">console.count</code> 用来观察这个组件是否重新渲染。\n得到的结果就是，<strong>foo 会随着每次点击都打印，而 bar 不会</strong>。如果将 memo 移除的话，每次 bar 也会被打印出来了。</p>\n<p>最后结论就是：<strong>Provider 的更新不会带动 Consumer 之间的组件更新</strong>。</p>\n<h3 id=\"顺便一提\" style=\"position:relative;\"><a href=\"#%E9%A1%BA%E4%BE%BF%E4%B8%80%E6%8F%90\" aria-label=\"顺便一提 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" 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>顺便一提</h3>\n<p>当然，其实这个问题通过推断就能得到结果的。因为新的 Context 带来的一个重要特性就是<strong>即使 shouldComponentUpdate 返回 false，子组件也会被更新。</strong></p>\n<p>如果我们把上面的 Foo 和 Bar 合成一个</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> Bar <span class=\"token operator\">=</span> <span class=\"token function\">memo</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  console<span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token string\">'bar'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"gatsby-highlight-code-line\">  <span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> <span class=\"token function\">useContext</span><span class=\"token punctuation\">(</span>FooContext<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 tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">i am </span><span class=\"token punctuation\">{</span>foo<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span><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>也是一样的效果。</p>","frontmatter":{"title":"一些 React 中的刁钻问题","spoiler":"收集一些不为人所知的，React 的细节问题。帮助自己理顺 React 内部深层的逻辑","image":null,"date":"2019/01/02"}}},"pageContext":{"slug":"/react-trap/","previous":{"fields":{"slug":"/react-lazy/"},"frontmatter":{"title":"React Lazy 的实现原理","image":null,"spoiler":"简单讨论一下 React.lazy 内部实现"}},"next":{"fields":{"slug":"/react-as-a-runtime/"},"frontmatter":{"title":"React as a UI Runtime","image":null,"spoiler":"An in-depth description of the React programming model."}}}}}