<p dir="auto"><strong>Do you want to request a <em>feature</em> or report a <em>bug</em>?</strong><br>
This is a bug report. I think this bug and one already reported (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="370404098" data-permission-text="Title is private" data-url="https://github.com/facebook/react/issues/13856" data-hovercard-type="issue" data-hovercard-url="/facebook/react/issues/13856/hovercard" href="https://github.com/facebook/react/issues/13856">#13856</a>) have the same root cause.</p>
<p dir="auto"><strong>What is the current behavior?</strong><br>
A component instance preventing its own rerender, by returning <code class="notranslate">false</code> from <code class="notranslate">shouldComponentUpdate</code> may under certain condition still get its children rerendered, even if no direct update to said children happened.</p>
<p dir="auto">This behaviour is triggered by a very specific stack of components. The simplest example I managed to create (<a href="https://codesandbox.io/s/9zymvq479r" rel="nofollow">https://codesandbox.io/s/9zymvq479r</a>) involves five components, each rendering the next one:</p>
<ul dir="auto">
<li><code class="notranslate">UpdatingRoot</code> responsible for triggering the top-level update, by calling <code class="notranslate">setState</code> in the <code class="notranslate">componentDidMount</code> lifecycle method.</li>
<li><code class="notranslate">LegacyContextInjector</code> responsible for creating the child context object according to the legacy context API.</li>
<li><code class="notranslate">SCUBarrier</code> with <code class="notranslate">shouldComponentUpdate</code> function always returning <code class="notranslate">false</code>. Any children of this component instance should not be rerendered as long as they themselves are not sheduled for an update. This is the behaviour I expect. This bug prevents it from happening.</li>
<li><code class="notranslate">IntermediateComponent</code> responsible for rendering <code class="notranslate">UpdatingChild</code> few levels down in the component instance tree.</li>
<li><code class="notranslate">UpdatingChild</code> responsible for calling its own <code class="notranslate">setState</code> in the <code class="notranslate">componentDidMount</code> lifecycle method.</li>
</ul>
<p dir="auto">Each of the created component instances will be rendered only one or two times. Both <code class="notranslate">UpdatingRoot</code> and <code class="notranslate">UpdatingChild</code> are expected to be rendered twice, and this does happen. Each of the <code class="notranslate">IntermediateComponent</code> instances is expected to render only once. This expectation is based on the fact that the <code class="notranslate">SCUBarrier#shouldComponentUpdate</code> always returns false, and no child <code class="notranslate">IntermediateComponent</code> instance updates itself. The legacy context API should not pierce through <code class="notranslate">shouldComponentUpdate</code> returning <code class="notranslate">false</code> (<a href="https://reactjs.org/docs/legacy-context.html#updating-context" rel="nofollow">https://reactjs.org/docs/legacy-context.html#updating-context</a>).</p>
<p dir="auto">Despite those expectation, all <code class="notranslate">IntermediateComponent</code> instances are rendered twice.</p>
<p dir="auto">It's important that the <code class="notranslate">UpdatingRoot#setState</code> and <code class="notranslate">UpdatingChild#setState</code> function are batched together. You can try "unbatching" them, by swapping:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="this.setState({ weAreHome: &quot;my friend&quot; });"><pre class="notranslate"><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-en">setState</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">weAreHome</span>: <span class="pl-s">"my friend"</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">for</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="setTimeout(() =&gt; this.setState({ weAreHome: &quot;my friend&quot; }),0)"><pre class="notranslate"><span class="pl-en">setTimeout</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-en">setState</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">weAreHome</span>: <span class="pl-s">"my friend"</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">,</span><span class="pl-c1">0</span><span class="pl-kos">)</span></pre></div>
<p dir="auto">When this is done the <code class="notranslate">IntermediateComponent</code> instances are correctly rendered only once.</p>
<p dir="auto">My example code does the batching by calling <code class="notranslate">UpdatingRoot#setState</code> and <code class="notranslate">UpdatingChild#setState</code> functions directly in the <code class="notranslate">componentDidMount</code> lifecycles of their respective components. I believe this generally is an anti-pattern, but this approach was chosen for simplicity. The same issue can be replicated by calling both <code class="notranslate">setState</code> in <code class="notranslate">ReactDOM.unstable_batchedUpdates(() =&gt; { ... })</code></p>
<p dir="auto">For a more practical example of a situation in which two ancestor-descendant components may naturally schedule updates in a batch, take a look at <a href="https://github.com/tappleby/redux-batched-subscribe">https://github.com/tappleby/redux-batched-subscribe</a>.</p>
<p dir="auto">Legacy context API is used by popular components like <code class="notranslate">Transition</code> from <a href="https://github.com/reactjs/react-transition-group">https://github.com/reactjs/react-transition-group</a> or by commonly used <code class="notranslate">react-redux</code>.</p>
<p dir="auto">A real-life example of this bug could be:<br>
Two Redux-connected component <code class="notranslate">A</code> and <code class="notranslate">C</code> are scheduled for update in the same batch. A <code class="notranslate">Transition</code> (from <code class="notranslate">react-transition-group</code>) injecting legacy context is rendered between them. Some component <code class="notranslate">B</code> in between <code class="notranslate">A</code> and <code class="notranslate">C</code> (descendent of <code class="notranslate">A</code>, ancestor of <code class="notranslate">C</code>) implements <code class="notranslate">shouldComponentUpdate</code> to optimize the rendering of some unchanging subtree (which eventually, down the line, renders <code class="notranslate">B</code>). Both <code class="notranslate">A</code> and <code class="notranslate">C</code> component instances rerender correctly (as expected, since <code class="notranslate">react-redux</code> uses legacy context API correctly as described in <a href="https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076" rel="nofollow">https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076</a>). However the instances corresponding to the unchanging element tree returned by <code class="notranslate">B</code> will also be unexpectedly rerendered.</p>
<p dir="auto"><strong>If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.</strong><br>
<a href="https://codesandbox.io/s/9zymvq479r" rel="nofollow">https://codesandbox.io/s/9zymvq479r</a></p>
<p dir="auto"><strong>What is the expected behavior?</strong><br>
All <code class="notranslate">IntermediateComponent</code> instances should be rendered only once.</p>
<p dir="auto"><strong>Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?</strong><br>
react "16.6.0-alpha.f47a958"<br>
react-dom "16.6.0-alpha.8af6728 - canary"</p>
<p dir="auto">I originally discovered this issue in the React/ReactDOM version 16.5.2</p>
<p dir="auto">Please let me know if there is something unclear about this bug. It's definitely not an easy thing to reproduce, since so many different components need to be involved!</p>
<p dir="auto"><strong>Do you want to request a <em>feature</em> or report a <em>bug</em>?</strong></p>
<p dir="auto">bug.</p>
<p dir="auto"><strong>What is the current behavior?</strong></p>
<p dir="auto">In a component tree where legacy context and new context are co-exisiting, when components are placed on tree nodes under the legacy context provider and between new context provider and consumer, components are always rendered even if <code class="notranslate">shouldComponentUpdate()</code> returns false in a parent component.</p>
<p dir="auto"><strong>If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.</strong></p>
<p dir="auto"><a href="https://jsfiddle.net/fdrgz9c4/15/" rel="nofollow">https://jsfiddle.net/fdrgz9c4/15/</a></p>
<p dir="auto">The above is the minimal case I've created without other dependencies, but first I found this behaviour in my Redux application, which uses legacy style context to map the state in store.</p>
<p dir="auto"><strong>What is the expected behavior?</strong></p>
<p dir="auto">Should not call <code class="notranslate">render()</code> when <code class="notranslate">shouldComponentUpdate()</code> returns false in parent components, as same as the other cases (it prevents rendering when it is located without legacy context or not between new context provider / consumer tree. See the above fiddle)</p>
<p dir="auto"><strong>Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?</strong></p>
<p dir="auto">React 16.5.2, maybe in older versions.<br>
No OS/browser dependencies I believe.</p>