1<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=device-width"/><meta charSet="utf-8"/><link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"/><link rel="manifest" href="/favicon/site.webmanifest"/><link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#000000"/><link rel="shortcut icon" href="/favicon/favicon.ico"/><meta name="msapplication-TileColor" content="#000000"/><meta name="msapplication-config" content="/favicon/browserconfig.xml"/><meta name="theme-color" content="#000"/><link rel="alternate" type="application/rss+xml" href="/feed.xml"/><meta name="description" content="Blog. by Hao Jiang"/><title>React Performance Recipes | Blog. by Hao Jiang</title><meta name="next-head-count" content="14"/><link rel="preload" href="/_next/static/css/561d2eb7b0c22905.css" as="style"/><link rel="stylesheet" href="/_next/static/css/561d2eb7b0c22905.css" data-n-g=""/><link rel="preload" href="/_next/static/css/ff87b2fb699a6b60.css" as="style"/><link rel="stylesheet" href="/_next/static/css/ff87b2fb699a6b60.css" data-n-p=""/><noscript data-n-css=""></noscript>
1<script defer="" nomodule="" src="/_next/static/chunks/polyfills-5cd94c89d3acac5f.js"></script>
1<script src="/_next/static/chunks/webpack-378e68e29c265886.js" defer=""></script>
1<script src="/_next/static/chunks/framework-91d7f78b5b4003c8.js" defer=""></script>
1<script src="/_next/static/chunks/main-3b630716291465af.js" defer=""></script>
1<script src="/_next/static/chunks/pages/_app-61dfa83d5790b473.js" defer=""></script>
1<script src="/_next/static/chunks/202-1e97c8d2e4f6ddc5.js" defer=""></script>
1<script src="/_next/static/chunks/88-6487fd74d238ccbf.js" defer=""></script>
1<script src="/_next/static/chunks/pages/posts/%5Bslug%5D-a250bb94cb2a7860.js" defer=""></script>
1<script src="/_next/static/nRYnRkVXs0kRjiu1HTnAF/_buildManifest.js" defer=""></script>
1<script src="/_next/static/nRYnRkVXs0kRjiu1HTnAF/_ssgManifest.js" defer=""></script>
1<script src="/_next/static/nRYnRkVXs0kRjiu1HTnAF/_middlewareManifest.js" defer=""></script>
1</head><body><div id="__next" data-reactroot=""><div class="min-h-screen"><div class="border-b bg-accent-1 border-accent-2"><div class="container mx-auto px-5 max-w-screen-870"><div class="py-2 text-sm text-center">The source code for this blog is<!-- --> <a href="https://github.com/Neo42/blog" class="underline transition-colors duration-200 hover:text-success">available on GitHub</a>.</div></div></div><main><div class="container mx-auto px-5 max-w-screen-870"><h2 class="mt-8 mb-20 text-2xl font-bold leading-tight tracking-tight md:text-4xl md:tracking-tighter"><a class="hover:underline" href="/">Blog.</a></h2><article class="mb-32"><h1 class="text-6xl md:text-7xl font-bold tracking-tighter leading-tight md:leading-none mb-12 text-center md:text-left">React Performance Recipes</h1><div class=""><div class="markdown-styles_markdown__h_8de"><div><h2>Problem: Unnecessary Callback Re-Initiation</h2><style class="ssmq-0">.ssmq-0.ssmq-default{display:block}.ssmq-0:not(.ssmq-default){display: none;} 2@media (min-width: 920px){.ssmq-0.ssmq-min-width920px{display:block}.ssmq-0:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-0 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>When you're using a callback function within <code>useEffect</code>, it's hard to predict how callback function will be modified in the future. Only passing in certain variables from the callback to the deps list won't do. Because once the variables passed in gets removed, your dependencies won't work any more. But you still have to provide some values to the dependency list to sync <code>useEffect</code> with the callback change.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>One way we can solve this is to include the entire callback function into the dependency list.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>But doing this also introduces another problem -- because the callback function is defined in the component's function body, it will be re-initialized from scratch every time the component gets rendered, and since functions are compared through references, even if the code of a function isn't modified between renders, every time it gets re-initialized, it will be different from it "previous" self in the last render of the component, which will trigger a infinite re-rendering loop that you definitely wouldn't like. And that difference is what some people call "referential inequality", which will also happen when you're working with objects and arrays, because these three types are structural types that are compared through references.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><h3>Solution: <code>React.useCallback</code></h3><p><code>const memoizedCallback = React.useCallback(callback, dependencyList)</code>: <strong>Memoizes a callback function, only re-initialized it when the dependencies change and avoids referential inequality.</strong></p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>And now every time the component gets re-rendered, the <code>updateLocalStorage</code> callback will only re-initialized when the <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code"><code>count</code><svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewB
2ox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> value changes, otherwise React will use the same copy of it.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p><code>useCallback</code> can be powerful if properly used, but it won't necessarily make a callback function better for performance. As you can see, it <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">causes extra memory allocation<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> and the memoized one won't be garbage collected after the render, which is something you should be aware of. So remember to only <code>useCallback</code> when you have to.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-0 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>When you're using a callback function within <code>useEffect</code>, it's hard to predict how callback function will be modified in the future. Only passing in certain variables from the callback to the deps list won't do. Because once the variables passed in gets removed, your dependencies won't work any more. But you still have to provide some values to the dependency list to sync <code>useEffect</code> with the callback change.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>One way we can solve this is to include the entire callback function into the dependency list.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">updateLocalStorage</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token comment">// <-- function as a dependency</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">updateLocalStorage</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token comment">// <-- function as a dependency</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>But doing this also introduces another problem -- because the callback function is defined in the component's function body, it will be re-initialized from scratch every time the component gets rendered, and since functions are compared through references, even if the code of a function isn't modified between renders, every time it gets re-initialized, it will be different from it "previous" self in the last render of the component, which will trigger a infinite re-rendering loop that you definitely wouldn't like. And that difference is what some people call "referential inequality", which will also happen when you're working with objects and arrays, because these three types are structural types that are compared through references.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">updateLocalStorage</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token comment">// changes in every render</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">updateLocalStorage</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token comment">// changes in every render</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><h3>Solution: <code>React.useCallback</code></h3><p><code>const memoizedCallback = React.useCallback(callback, dependencyList)</code>: <strong>Memoizes a callback function, only re-initialized it when the dependencies change and avoids referential inequality.</strong></p></div><div class="ch-hike-step"><p>And now every time the component gets re-rendered, the <code>updateLocalStorage</code> callback will only re-initialized when the <code>count</code> value changes, otherwise React will use the same copy of it.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> window</span><span class="token punctuation">.</span><span class="token plain">localStorage</span><span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span><span class="token plain"> count</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p><code>useCallback</code> can be powerful if properly used, but it won't necessarily make a callback function better for performance. As you can see, it <!-- -->causes extra memory allocation<!-- --> and the memoized one won't be garbage collected after the render, which is something you should be aware of. So remember to only <code>useCallback</code> when you have to.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
2<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token keyword">const</span><span class="token plain"> memoizedCallback </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useCallback</span><span class="token punctuation">(</span><span class="token plain">callback</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">someValue</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token keyword">const</span><span class="token plain"> memoizedCallback </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useCallback</span><span class="token punctuation">(</span><span class="token plain">callback</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">someValue</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div></section></div>
2<script class="ssmq-0">(function() { 3 var qs = ["(min-width: 920px)","default"]; 4 var clss = ["ssmq-min-width920px","ssmq-default"]; 5 var mainCls = "ssmq-0"; 6 7 var scrEls = document.getElementsByTagName("script"); 8 var scrEl = scrEls[scrEls.length - 1]; 9 var parent = scrEl.parentNode; 10 11 var el = null; 12 for (var i = 0; i < qs.length - 1; i++) { 13 if (window.matchMedia(qs[i]).matches) { 14 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 15 break; 16 } 17 } 18 if (!el) { 19 var defaultClass = clss.pop(); 20 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 21 } 22 el.removeAttribute("class"); 23 24 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 25 parent.removeChild(e); 26 }); 27})();</script>
27<h2>Problem: Unnecessary Expensive Calculation</h2><style class="ssmq-1">.ssmq-1.ssmq-default{display:block}.ssmq-1:not(.ssmq-default){display: none;} 28@media (min-width: 920px){.ssmq-1.ssmq-min-width920px{display:block}.ssmq-1:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-1 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>React comes with the concept of "rendering". When a component is being rendered by React, that means React is calling the component function (or the <code>render</code> method in the case of class components). This comes with an unfortunate limitation that calculations performed within the function (or <code>render</code>) will be performed every single render, regardless of whether the inputs for the calculations change. For example:</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>Every time the parent component of <code>MyComponent</code> is re-rendered or some state is added later and setting the state triggers re-renders, so for <code>expensiveCalculation</code>, <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">it will be recalculated in every render<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> of this component, which leads to performance bottlenecks.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><h3>Solution: <code>React.useMemo</code></h3><p><code>const memoizedResult = React.useMemo(calculationCallback, dependencyList)</code>: <strong>Memoizes the result of the calculation and only calls the calculation when the dependencies changes.</strong></p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>With <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code"><code>useMemo</code><svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>, here is the optimized version of <code>MyComponent</code>. And now <code>result</code> will only be calculated again when <code>x</code> or <code>y</code> changes.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
28<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">return</span><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token plain">Hello World</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">return</span><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token plain">Hello World</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-1 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>React comes with the concept of "rendering". When a component is being rendered by React, that means React is calling the component function (or the <code>render</code> method in the case of class components). This comes with an unfortunate limitation that calculations performed within the function (or <code>render</code>) will be performed every single render, regardless of whether the inputs for the calculations change. For example:</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
28<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">return</span><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token plain">Hello World</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">return</span><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token plain">Hello World</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>Every time the parent component of <code>MyComponent</code> is re-rendered or some state is added later and setting the state triggers re-renders, so for <code>expensiveCalculation</code>, <!-- -->it will be recalculated in every render<!-- --> of this component, which leads to performance bottlenecks.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
28<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function">expensiveCalculation</span><span class="token punctuation">(</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function">expensiveCalculation</span><span class="token punctuation">(</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><h3>Solution: <code>React.useMemo</code></h3><p><code>const memoizedResult = React.useMemo(calculationCallback, dependencyList)</code>: <strong>Memoizes the result of the calculation and only calls the calculation when the dependencies changes.</strong></p></div><div class="ch-hike-step"><p>With <code>useMemo</code>, here is the optimized version of <code>MyComponent</code>. And now <code>result</code> will only be calculated again when <code>x</code> or <code>y</code> changes.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
28<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">expensiveCalculation</span><span class="token punctuation">(</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">expensiveCalculation</span><span class="token punctuation">(</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain">x</span><span class="token punctuation">,</span><span class="token plain"> y</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div></section></div>
28<script class="ssmq-1">(function() { 29 var qs = ["(min-width: 920px)","default"]; 30 var clss = ["ssmq-min-width920px","ssmq-default"]; 31 var mainCls = "ssmq-1"; 32 33 var scrEls = document.getElementsByTagName("script"); 34 var scrEl = scrEls[scrEls.length - 1]; 35 var parent = scrEl.parentNode; 36 37 var el = null; 38 for (var i = 0; i < qs.length - 1; i++) { 39 if (window.matchMedia(qs[i]).matches) { 40 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 41 break; 42 } 43 } 44 if (!el) { 45 var defaultClass = clss.pop(); 46 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 47 } 48 el.removeAttribute("class"); 49 50 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 51 parent.removeChild(e); 52 }); 53})();</script>
53<h2>So when should I <code>useMemo</code> and <code>useCallback</code>?</h2><p>Use <code>useCallback</code> when you want to avoid unnecessary re-renders caused by <strong>referential inequality</strong> of structural data types, for example:</p><style class="ssmq-2">.ssmq-2.ssmq-default{display:block}.ssmq-2:not(.ssmq-default){display: none;} 54@media (min-width: 920px){.ssmq-2.ssmq-min-width920px{display:block}.ssmq-2:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-2 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>When using <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">values<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> as <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">dependencies<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> in <code>useEffect</code>.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>When using <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code"><code>React.memo</code><svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> to avoid unnecessary child re-renders when you pass complex <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">callbacks<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> as event handlers into child components. Because callback functions will be re-initialized every re-render.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>Use <code>useMemo</code> when you want to avoid unnecessary computationally expensive <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">calculations<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
54<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> baz </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> baz </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-2 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>When using <!-- -->values<!-- --> as <!-- -->dependencies<!-- --> in <code>useEffect</code>.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
54<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> baz </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> baz </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">useMemo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>When using <code>React.memo</code> to avoid unnecessary child re-renders when you pass complex <!-- -->callbacks<!-- --> as event handlers into child components. Because callback functions will be re-initialized every re-render.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
54<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token keyword">const</span><span class="token plain"> CountButton </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">memo</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token plain"> </span><span class="token function">CountButton</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token parameter">onClick</span><span class="token punctuation">,</span><span class="token parameter"> count</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token punctuation">{</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token keyword">const</span><span class="token plain"> CountButton </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</span><span class="token function">memo</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token plain"> </span><span class="token function">CountButton</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token parameter">onClick</span><span class="token punctuation">,</span><span class="token parameter"> count</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token punctuation">{</span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>Use <code>useMemo</code> when you want to avoid unnecessary computationally expensive <!-- -->calculations<!-- -->.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
54<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">calculatePrimes</span><span class="token punctuation">(</span><span class="token plain">iterations</span><span class="token punctuation">,</span><span class="token plain"> multiplier</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">calculatePrimes</span><span class="token punctuation">(</span><span class="token plain">iterations</span><span class="token punctuation">,</span><span class="token plain"> multiplier</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br/></div></div></code></pre></div></div></div></div></div></div></div></section></div>
54<script class="ssmq-2">(function() { 55 var qs = ["(min-width: 920px)","default"]; 56 var clss = ["ssmq-min-width920px","ssmq-default"]; 57 var mainCls = "ssmq-2"; 58 59 var scrEls = document.getElementsByTagName("script"); 60 var scrEl = scrEls[scrEls.length - 1]; 61 var parent = scrEl.parentNode; 62 63 var el = null; 64 for (var i = 0; i < qs.length - 1; i++) { 65 if (window.matchMedia(qs[i]).matches) { 66 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 67 break; 68 } 69 } 70 if (!el) { 71 var defaultClass = clss.pop(); 72 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 73 } 74 el.removeAttribute("class"); 75 76 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 77 parent.removeChild(e); 78 }); 79})();</script>
79<h2>Problem: Unnecessary Child Re-Renders</h2><p>Besides function re-initiation and value recalculation, there is another behavior that can lead to performance bottlenecks: <strong>unnecessary re-renders</strong>. As I said before, rendering means that React is calling your component function or its <code>render</code> method. React renders a component to recalculate the data to reflect the changes within the component. In what circumstances will a re-render be unnecessary?</p><p>There are four reasons for React to re-render a component:</p><ol><li>The <strong>state</strong> of the component has changed, which must be triggered with the state setting function.</li><li>The <strong>props</strong> the component receives have changed.</li><li>The <strong>context</strong> values the component uses have changed.</li><li>The component's <strong>parent</strong> has been re-rendered because of the reasons above.</li></ol><p>Now the first three can't and also shouldn't be avoided because those are basically data changes, which must be recalculated and displayed on the screen. But the last one is, most of the time, unnecessary. When a parent component changes, if there is no change to be applied on the child, there is no need to call the component function to reflect any data changes.</p><style class="ssmq-3">.ssmq-3.ssmq-default{display:block}.ssmq-3:not(.ssmq-default){display: none;} 80@media (min-width: 920px){.ssmq-3.ssmq-min-width920px{display:block}.ssmq-3:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-3 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>Here is an example:</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>When you click the <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code"><code>CountButton</code><svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>, it changes the <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">state<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> of its parent <code>Example</code>, which triggers a re-render for <code>Example</code>, which in turn leads to the unnecessary re-render of the <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code"><code>NameInput</code><svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> even though none of its props has changed.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>So how do we solve this problem?</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><h3>Solution: <code>React.memo</code></h3><p><code>React.memo(component)</code>: memoize a component and only re-render it when the props it receives changes.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>To prevent the unnecessary re-render of <code>NameInput</code>, we can wrap it with <code>React.memo</code> as a bail-out. Like <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewB
80ox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>:</p><p>By doing this we let React know that it doesn't need a re-render until at least one of its props changes.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><h4><code>React.memo</code>: Might not work like a charm</h4><p>You might want to ask what if we do this to the <code>CountButton</code>. Here is the answer: If we were to only wrap it into <code>React.memo</code> like <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>, <em>it wouldn't work</em>, because <code>increment</code> always gets re-initialized every time <code>Example</code> gets re-rendered, which makes the <code>onClick</code> prop of <code>CountButton</code> changes all the time due to referential inequality. But <code>React.memo</code> only prevent re-renders when all the props stay the same.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p><strong>Unless</strong> we also wrap the <code>increment</code> function within the <code>Example</code> with <code>React.useCallback</code>. Like <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>:</p><p>Just so you know that it's better to use <code>React.memo</code> more mindfully rather than shove everything into it.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-3 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>Here is an example:</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>When you click the <code>CountButton</code>, it changes the <!-- -->state<!-- --> of its parent <code>Example</code>, which triggers a re-render for <code>Example</code>, which in turn leads to the unnecessary re-render of the <code>NameInput</code> even though none of its props has changed.</p></div><div class="ch-hike-step"><p>So how do we solve this problem?</p></div><div class="ch-hike-step"><h3>Solution: <code>React.memo</code></h3><p><code>React.memo(component)</code>: memoize a component and only re-render it when the props it receives changes.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p>To prevent the unnecessary re-render of <code>NameInput</code>, we can wrap it with <code>React.memo</code> as a bail-out. Like <!-- -->this<!-- -->:</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div><p>By doing this we let React know that it doesn't need a re-render until at least one of its props changes.</p></div><div class="ch-hike-step"><h4><code>React.memo</code>: Might not work like a charm</h4><p>You might want to ask what if we do this to the <code>CountButton</code>. Here is the answer: If we were to only wrap it into <code>React.memo</code> like <!-- -->this<!-- -->, <em>it wouldn't work</em>, because <code>increment</code> always gets re-initialized every time <code>Example</code> gets re-rendered, which makes the <code>onClick</code> prop of <code>CountButton</code> changes all the time due to referential inequality. But <code>React.memo</code> only prevent re-renders when all the props stay the same.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">div</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-punctuation">`</span><span class="token interpolation-punctuation">${</span><span class="token interpolation">name</span><span class="token interpolation-punctuation">}</span><span class="token string">'s favorite number is </span><span class="token interpolation-punctuation">${</span><span class="token interpolation">count</span><span class="token interpolation-punctuation">}</span><span class="token template-punctuation">`</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div><div class="ch-hike-step"><p><strong>Unless</strong> we also wrap the <code>increment</code> function within the <code>Example</code> with <code>React.useCallback</code>. Like <!-- -->this<!-- -->:</p><p>Just so you know that it's better to use <code>React.memo</code> more mindfully rather than shove everything into it.</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
80<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> increment </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</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 plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">setCount</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> c </span><span class="token operator">+</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token keyword">const</span><span class="token plain"> increment </span><span class="token operator">=</span><span class="token plain"> React</span><span class="token punctuation">.</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 plain"> </span><span class="token operator">=></span><span class="token plain"> </span><span class="token function">setCount</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token operator">=></span><span class="token plain"> c </span><span class="token operator">+</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br/></div></div></code></pre></div></div></div></div></div></div></div></section></div>
80<script class="ssmq-3">(function() { 81 var qs = ["(min-width: 920px)","default"]; 82 var clss = ["ssmq-min-width920px","ssmq-default"]; 83 var mainCls = "ssmq-3"; 84 85 var scrEls = document.getElementsByTagName("script"); 86 var scrEl = scrEls[scrEls.length - 1]; 87 var parent = scrEl.parentNode; 88 89 var el = null; 90 for (var i = 0; i < qs.length - 1; i++) { 91 if (window.matchMedia(qs[i]).matches) { 92 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 93 break; 94 } 95 } 96 if (!el) { 97 var defaultClass = clss.pop(); 98 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 99 } 100 el.removeAttribute("class"); 101 102 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 103 parent.removeChild(e); 104 }); 105})();</script>
105<style class="ssmq-4">.ssmq-4.ssmq-default{display:block}.ssmq-4:not(.ssmq-default){display: none;} 106@media (min-width: 920px){.ssmq-4.ssmq-min-width920px{display:block}.ssmq-4:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-4 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><h4><code>React.memo</code>: Use a custom comparator function</h4><p>Most of the time, a simple <code>React.memo</code> works just fine, there are times when its default behavior isn't what you desire.</p><p>Imagine you are rendering a menu containing a list of items, and the user can highlight a item at a time. Your <code>Menu</code> and <code>ListItem</code> components might look like <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>.</p><p>Both of these components are wrapped into <code>React.memo</code> <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">already<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>. So now you might expect this way the performance has been optimized to its highest level. But here is a problem still unsolved: when the user highlights a different item, besides the the previously and newly highlighted items, all the other untouched items will still be re-rendered. And that's because every time the user highlights a different item, they changes the <code>highlightedIndex</code> prop, which triggers the re-render of each item, no matter if it was highlighted previously or is highlighted now or never has been touched the whole time.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>To solve this problem, you'll need to change how React should compare props over time. And this is where the <strong>custom comparator</strong> comes into play. You can pass a custom comparator as the second argument into <code>React.memo</code> so that it uses your, hopefully better, rules, instead of its default rules, for prop comparison. For example, for the <code>Menu</code> and <code>ListItem</code>, we can do <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>:</p><p>This way the <code>ListItem</code> won't be re-rendered unless it is involved in highlight changes.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><h4><code>React.memo</code>: Parent keeps the calculation, children keep the primitive props</h4><p>You might notice the logic in the custom comparator above is quite mind-boggling, especially when you are working on a big project with a team of many people. So for that scenario, one takeaway you might find valuable is that, if you're rendering a ton of instances of a particular component, try to do calculations a little higher -- maybe in the parent or even higher -- so you only need to pass primitive values to the component and let those value changes trigger DOM updates. That way you won't have to worry about breaking memoization or create a custom comparator, which can save you quite some code and brain cells. For example, we can simplify the code above like <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">this<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewB
106ox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button>:</p><p>Notice how we move the details of the highlight and selection calculation up to the parent component <code>Menu</code>, only pass two boolean props <code>isHighlighted</code> and <code>isSelected</code> to the child component <code>ListItem</code> and don't even have to write a custom comparator function.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
106<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token literal-property">backgroundColor</span><span class="token operator">:</span><span class="token plain"> isHighlighted </span><span class="token operator">?</span><span class="token plain"> </span><span class="token string">'lightgray'</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token string">'inherit'</span><span class="token punctuation">,</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token literal-property">backgroundColor</span><span class="token operator">:</span><span class="token plain"> isHighlighted </span><span class="token operator">?</span><span class="token plain"> </span><span class="token string">'lightgray'</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token string">'inherit'</span><span class="token punctuation">,</span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-4 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><h4><code>React.memo</code>: Use a custom comparator function</h4><p>Most of the time, a simple <code>React.memo</code> works just fine, there are times when its default behavior isn't what you desire.</p><p>
106Imagine you are rendering a menu containing a list of items, and the user can highlight a item at a time. Your <code>Menu</code> and <code>ListItem</code> components might look like <!-- -->this<!-- -->.</p><p>Both of these components are wrapped into <code>React.memo</code> <!-- -->already<!-- -->. So now you might expect this way the performance has been optimized to its highest level. But here is a problem still unsolved: when the user highlights a different item, besides the the previously and newly highlighted items, all the other untouched items will still be re-rendered. And that's because every time the user highlights a different item, they changes the <code>highlightedIndex</code> prop, which triggers the re-render of each item, no matter if it was highlighted previously or is highlighted now or never has been touched the whole time.</p></div><div class="ch-hike-step"><p>To solve this problem, you'll need to change how React should compare props over time. And this is where the <strong>custom comparator</strong> comes into play. You can pass a custom comparator as the second argument into <code>React.memo</code> so that it uses your, hopefully better, rules, instead of its default rules, for prop comparison. For example, for the <code>Menu</code> and <code>ListItem</code>, we can do <!-- -->this<!-- -->:</p><p>This way the <code>ListItem</code> won't be re-rendered unless it is involved in highlight changes.</p></div><div class="ch-hike-step"><h4><code>React.memo</code>: Parent keeps the calculation, children keep the primitive props</h4><p>You might notice the logic in the custom comparator above is quite mind-boggling, especially when you are working on a big project with a team of many people. So for that scenario, one takeaway you might find valuable is that, if you're rendering a ton of instances of a particular component, try to do calculations a little higher -- maybe in the parent or even higher -- so you only need to pass primitive values to the component and let those value changes trigger DOM updates. That way you won't have to worry about breaking memoization or create a custom comparator, which can save you quite some code and brain cells. For example, we can simplify the code above like <!-- -->this<!-- -->:</p><div class="ch-hike-step-code" style="zoom:0.8"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
106<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token literal-property">backgroundColor</span><span class="token operator">:</span><span class="token plain"> isHighlighted </span><span class="token operator">?</span><span class="token plain"> </span><span class="token string">'lightgray'</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token string">'inherit'</span><span class="token punctuation">,</span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token literal-property">backgroundColor</span><span class="token operator">:</span><span class="token plain"> isHighlighted </span><span class="token operator">?</span><span class="token plain"> </span><span class="token string">'lightgray'</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token string">'inherit'</span><span class="token punctuation">,</span><br/></div></div></code></pre></div></div></div></div></div></div><p>Notice how we move the details of the highlight and selection calculation up to the parent component <code>Menu</code>, only pass two boolean props <code>isHighlighted</code> and <code>isSelected</code> to the child component <code>ListItem</code> and don't even have to write a custom comparator function.</p></div></section></div>
106<script class="ssmq-4">(function() { 107 var qs = ["(min-width: 920px)","default"]; 108 var clss = ["ssmq-min-width920px","ssmq-default"]; 109 var mainCls = "ssmq-4"; 110 111 var scrEls = document.getElementsByTagName("script"); 112 var scrEl = scrEls[scrEls.length - 1]; 113 var parent = scrEl.parentNode; 114 115 var el = null; 116 for (var i = 0; i < qs.length - 1; i++) { 117 if (window.matchMedia(qs[i]).matches) { 118 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 119 break; 120 } 121 } 122 if (!el) { 123 var defaultClass = clss.pop(); 124 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 125 } 126 el.removeAttribute("class"); 127 128 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 129 parent.removeChild(e); 130 }); 131})();</script>
131<h2>Problem: Rendering huge lists with large quantities of data</h2><p>As we learned before, React is pretty optimized itself and provides a suite of performance optimization tools for you to use. But if you were to make huge updates to the DOM, there is little React can do because there is just too much to do. This problem is always revealed by UIs like data visualization, grids, tables, and lists with lots of data. Thereâs only so much you can do before we have to conclude that weâre simply running too much code (or running the same small amount of code too many times).</p><h3>Solution: Windowing</h3><p>But it's still possible for us to work around this: when the user scroll through a huge table, chances are that they are only gonna see a portion of it, and that portion they are viewing won't be bigger than their window size. So what we can do is just to fetch a tiny part of the whole data set and render only for that portion exposed to the user, and as the user scroll through the list, we just "lazy" fetch the additional data, render the newly needed UI, and destroy the unwanted part just in time. Because the "lazy" rendered part of the UI is no bigger than the user's window size, this technique is known as "windowing", aka "virtualization". This works perfectly for this particular problem and can save you a ton of computational power.</p><p><img src="https://user-images.githubusercontent.com/22409868/148178031-d8b785e6-e706-4f8f-9d24-2efc105e3ada.png" alt="Windowing a list"/></p><p><img src="https://user-images.githubusercontent.com/22409868/148177888-9e3dadde-dd42-4e15-89a4-575fcb15d536.png" alt="Windowing a grid (table)"/></p><p>There are many libraries that allow you to use this "windowing" technique, such as <a target="_blank" rel="noopener noreferrer" href="https://react-window.vercel.app/#/examples/list/fixed-size" class="text-blue-700"><code>react-window</code></a> and <a target="_blank" rel="noopener noreferrer" href="https://bvaughn.github.io/react-virtualized/" class="text-blue-700"><code>react-virtualized</code></a>. These two are older compared to <a target="_blank" rel="noopener noreferrer" href="https://react-virtual.tanstack.com/" class="text-blue-700"><code>react-virtual</code></a>, which can be added to your project with one simple hook and supports all kinds of customization, including vertical, horizontal, grid, fixed, variable, dynamic, smooth and even infinite virtualization. Definitely give it a try!</p><style class="ssmq-5">.ssmq-5.ssmq-default{display:block}.ssmq-5:not(.ssmq-default){display: none;} 132@media (min-width: 920px){.ssmq-5.ssmq-min-width920px{display:block}.ssmq-5:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-5 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>Here is a regular data list component.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>And here is the windowed version with <code>react-virtual</code>.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div>
132<div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">li key</span><span class="token operator">=</span><span class="token punctuation">{</span><span class="token plain">item</span><span class="token punctuation">.</span><span class="token plain">id</span><span class="token punctuation">}</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token plain">item</span><span class="token punctuation">.</span><span class="token plain">name</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">li</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">li key</span><span class="token operator">=</span><span class="token punctuation">{</span><span class="token plain">item</span><span class="token punctuation">.</span><span class="token plain">id</span><span class="token punctuation">}</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token plain">item</span><span class="token punctuation">.</span><span class="token plain">name</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token plain">li</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-5 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>Here is a regular data list component.</p></div><div class="ch-hike-step"><p>And here is the windowed version with <code>react-virtual</code>.</p></div></section></div>
132<script class="ssmq-5">(function() { 133 var qs = ["(min-width: 920px)","default"]; 134 var clss = ["ssmq-min-width920px","ssmq-default"]; 135 var mainCls = "ssmq-5"; 136 137 var scrEls = document.getElementsByTagName("script"); 138 var scrEl = scrEls[scrEls.length - 1]; 139 var parent = scrEl.parentNode; 140 141 var el = null; 142 for (var i = 0; i < qs.length - 1; i++) { 143 if (window.matchMedia(qs[i]).matches) { 144 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 145 break; 146 } 147 } 148 if (!el) { 149 var defaultClass = clss.pop(); 150 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 151 } 152 el.removeAttribute("class"); 153 154 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 155 parent.removeChild(e); 156 }); 157})();</script>
157<h2>Problem: Global state changes trigger slow batch re-renders</h2><p>Optimizing performance for a few components are relatively easy, compared to doing that for many different components when you use contexts or global state management tools, like Redux, to manage the state of your apps. This scenario doesn't give you a clear clue to fix the problem because none of the components are slow in isolation but they update slow together when there is a global state change. The root of the problem is that there are just too many components responding to the state update and many of them shouldn't even do that.</p><p>One way to work around this is to "colocate" the global state down to each component, which works but can be quite some work for you if in a big project. So how can we make every component only re-render when the state it really cares about update?</p><h3>Solution: Use Recoil</h3><p><a target="_blank" rel="noopener noreferrer" href="https://recoiljs.org/" class="text-blue-700">Recoil</a> allow you to connect to the exact piece of state, which is a huge performance benefit. Some people also suggest using <
157a target="_blank" rel="noopener noreferrer" href="https://mobx.js.org/README.html" class="text-blue-700">MobX</a>, and as the author of MobX Michel Weststrate <a target="_blank" rel="noopener noreferrer" href="https://twitter.com/mweststrate/status/1261369870152871937" class="text-blue-700">tweeted</a>, both Recoil and MobX solves the problem of efficient rendering widely shared state, the problem that "React (Context), Redux and most state management libs don't solve." But I would recommend you to use Recoil with React. Because Recoil is built on top of React primitives, which makes it lean, flexible, and integrates closely with React's concurrent mode. It also handles data in an async way in a quasi-functional manner and integrates better with React hooks. In short, <em>Recoil thinks the way that React does</em>. So going with Recoil won't be a bad decision for you if you want to manage complex state performantly with React.</p><h2>Problem: Monitoring performance in production</h2><p>Things happen in production, especially for performance problems. There will be situations in which some unexpected mistake slips through the code review process and causes a performance problem. On the other hand, we can't make every user install the React devtool and profile the app for us as they use it. How are we gonna monitor things like that?</p><h3>Solution: Use the <code><React.Profiler></code></h3><p>The React team has created an <a target="_blank" rel="noopener noreferrer" href="https://reactjs.org/docs/profiler.html" class="text-blue-700"><code><React.Profiler></code></a> API specifically for situation like this. It doesnât give us quite as much information as the React DevTools do, but it does give us some useful information.</p><style class="ssmq-6">.ssmq-6.ssmq-default{display:block}.ssmq-6:not(.ssmq-default){display: none;} 158@media (min-width: 920px){.ssmq-6.ssmq-min-width920px{display:block}.ssmq-6:not(.ssmq-min-width920px){display: none;}}</style><div class="ssmq-6 ssmq-min-width920px"><section class="ch-hike ch-hike-fluid"><div class="ch-hike-content"><div class="ch-hike-step ch-hike-step-focused"><div class="ch-hike-step-content ch-hike-step-content-focused border-none"><p>Here is a basic example:</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>Wrap the target component within the <code>Profiler</code> to enable this feature. Itâs important to note that unless you build your app using <code>react-dom/profiling</code> and <code>scheduler/tracing-profiling</code> this component wonât do anything.</p></div></div><div class="ch-hike-step ch-hike-step-unfocused"><div class="ch-hike-step-gap"></div><div class="ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity"><p>For the <code>onRender</code> prop, you'll need to pass a callback function with a particular <button class="ch-hike-focus ch-hike-focus-inactive" title="Show code">signature<svg fill="none" stroke="currentColor" class="ch-hike-focus-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"></path></svg></button> otherwise the profiler component won't work. The <code>id</code> prop of the Profiler tree that has just committed. 159You can trace the <code>phase</code> of either "mount" (if the tree just mounted) or "update" (if it re-rendered. <code>actualDuration</code> is the time spent rendering the committed update. <code>baseDuration</code> is the estimated time to render the entire subtree without memoization. <code>startTime</code> is when React began rendering this update. <code>commitTime</code> is when React committed this update. <code>interactions</code> is the Set of interactions belonging to this update. In the callback y
159ou can do anything you want with the data, including do calculation, pass the result to your backend, aggregate or log render timings and so on.</p></div></div></div><aside class="ch-hike-sticker-column"><div class="ch-hike-sticker"><div class="ch-hike-editor"><div style="height:100%" class="ch-editor-frame undefined"><div class="ch-frame"><div class="ch-frame-title-bar bg-editor-bg"><div class="ch-frame-buttons"><div class="ch-frame-button ch-frame-button-left bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg"></div><div class="ch-frame-button-space"></div><div class="ch-frame-button ch-frame-button-right bg-button-bg border-button-bg"></div></div><div title="App.js" data-ch-tab="north" class="ch-editor-tab ch-editor-tab-active border-none bg-editor-bg"><div>App.js</div></div><div style="flex:1"></div></div><div class="ch-frame-content"><div class="ch-frame-zoom" style="--ch-frame-zoom:1;overflow:unset"><div data-ch-panel="north" class="ch-editor-body" style="height:100%"><pre class="language-js" style="opacity:0;overflow:hidden"><code><div class="prev-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">Navigation </span><span class="token punctuation">{</span><span class="token operator">...</span><span class="token plain">props</span><span class="token punctuation">}</span><span class="token plain"> </span><span class="token operator">/</span><span class="token operator">></span><br/></div></div><div class="next-longest-line"><div style="display:inline-block"><span class="token plain"> </span><span class="token operator"><</span><span class="token plain">Navigation </span><span class="token punctuation">{</span><span class="token operator">...</span><span class="token plain">props</span><span class="token punctuation">}</span><span class="token plain"> </span><span class="token operator">/</span><span class="token operator">></span><br/></div></div></code></pre></div></div></div></div></div></div></div></aside></section></div><div class="ssmq-6 ssmq-default"><section class="ch-hike ch-hike-fixed"><div class="ch-hike-step"><p>Here is a basic example:</p></div><div class="ch-hike-step"><p>Wrap the target component within the <code>Profiler</code> to enable this feature. Itâs important to note that unless you build your app using <code>react-dom/profiling</code> and <code>scheduler/tracing-profiling</code> this component wonât do anything.</p></div><div class="ch-hike-step"><p>For the <code>onRender</code> prop, you'll need to pass a callback function with a particular <!-- -->signature<!-- --> otherwise the profiler component won't work. The <code>id</code> prop of the Profiler tree that has just committed. 160You can trace the <code>phase</code> of either "mount" (if the tree just mounted) or "update" (if it re-rendered. <code>actualDuration</code> is the time spent rendering the committed update. <code>baseDuration</code> is the estimated time to render the entire subtree without memoization. <code>startTime</code> is when React began rendering this update. <code>commitTime</code> is when React committed this update. <code>interactions</code> is the Set of interactions belonging to this update. In the callback you can do anything you want with the data, including do calculation, pass the result to your backend, aggregate or log render timings and so on.</p></div></section></div>
160<script class="ssmq-6">(function() { 161 var qs = ["(min-width: 920px)","default"]; 162 var clss = ["ssmq-min-width920px","ssmq-default"]; 163 var mainCls = "ssmq-6"; 164 165 var scrEls = document.getElementsByTagName("script"); 166 var scrEl = scrEls[scrEls.length - 1]; 167 var parent = scrEl.parentNode; 168 169 var el = null; 170 for (var i = 0; i < qs.length - 1; i++) { 171 if (window.matchMedia(qs[i]).matches) { 172 el = parent.querySelector(":scope > ." + mainCls + "." + clss[i]); 173 break; 174 } 175 } 176 if (!el) { 177 var defaultClass = clss.pop(); 178 el = parent.querySelector(":scope > ." + mainCls + "." + defaultClass); 179 } 180 el.removeAttribute("class"); 181 182 parent.querySelectorAll(":scope > ." + mainCls).forEach(function (e) { 183 parent.removeChild(e); 184 }); 185})();</script>
185<p>I might write about more things on React performance, such as devtools, React Query, browser performance profiler and so on. Stay tuned.</p></div></div></div></article></div></main></div></div>
185<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"source":{"compiledSource":"\"use strict\";\n\nvar _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i \u003c arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i \u003c sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) \u003e= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i \u003c sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) \u003e= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar layoutProps = {};\nvar MDXLayout = \"wrapper\";\n\nfunction MDXContent(_ref) {\n var components = _ref.components,\n props = _objectWithoutProperties(_ref, _excluded);\n\n return mdx(MDXLayout, _extends({}, layoutProps, props, {\n components: components,\n mdxType: \"MDXLayout\"\n }), mdx(\"h2\", null, \"Problem: Unnecessary Callback Re-Initiation\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"const updateLocalStorage = () =\u003e\\n window.localStorage.setItem('count', count)\\n\\nReact.useEffect(() =\u003e {\\n updateLocalStorage()\\n}, []) // what goes in that dependency list?\\n\"))), mdx(\"p\", null, \"When you're using a callback function within \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useEffect\"), \", it's hard to predict how callback function will be modified in the future. Only passing in certain variables from the callback to the deps list won't do. Because once the variables passed in gets removed, your dependencies won't work any more. But you still have to provide some values to the dependency list to sync \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useEffect\"), \" with the callback change.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"const updateLocalStorage = () =\u003e\\n window.localStorage.setItem('count', count)\\n\\nReact.useEffect(() =\u003e {\\n updateLocalStorage()\\n}, [updateLocalStorage]) // \u003c-- function as a dependency\\n\"))), mdx(\"p\", null, \"One way we can solve this is to include the entire callback function into the dependency list.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"// recreated in every render\\nconst updateLocalStorage = () =\u003e\\n window.localStorage.setItem('count', count)\\n\\nReact.useEffect(() =\u003e {\\n updateLocalStorage()\\n}, [updateLocalStorage]) // changes in every render\\n\"))), mdx(\"p\", null, \"But doing this also introduces another problem -- because the callback function is defined in the component's function body, it will be re-initialized from scratch every time the component gets rendered, and since functions are compared through references, even if the code of a function isn't modified between renders, every time it gets re-initialized, it will be different from it \\\"previous\\\" self in the last render of the component, which will trigger a infinite re-rendering loop that you definitely wouldn't like. And that difference is what some people call \\\"referential inequality\\\", which will also happen when you're working with objects and arrays, because these three types are structural types that are compared through references.\"), mdx(CodeSlot, {\n style: {\
185n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"// how to optimize this with useCallback?\\nconst updateLocalStorage = () =\u003e\\n window.localStorage.setItem('count', count)\\n\\nReact.useEffect(() =\u003e {\\n updateLocalStorage()\\n}, [updateLocalStorage])\\n\"))), mdx(\"h3\", null, \"Solution: \", mdx(\"inlineCode\", {\n parentName: \"h3\"\n }, \"React.useCallback\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"const memoizedCallback = React.useCallback(callback, dependencyList)\"), \": \", mdx(\"strong\", {\n parentName: \"p\"\n }, \"Memoizes a callback function, only re-initialized it when the dependencies change and avoids referential inequality.\")), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"const updateLocalStorage = React.useCallback(\\n () =\u003e window.localStorage.setItem('count', count),\\n [count] // \u003c-- yup! That's a dependency list!\\n)\\n\\nReact.useEffect(() =\u003e {\\n updateLocalStorage()\\n}, [updateLocalStorage])\\n\"))), mdx(\"p\", null, \"And now every time the component gets re-rendered, the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"updateLocalStorage\"), \" callback will only re-initialized when the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://7\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"count\")), \" value changes, otherwise React will use the same copy of it.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"// a callback function\\nconst callback = () =\u003e {}\\n\\n// vs its memoized version\\nconst memoizedCallback = React.useCallback(callback, [someValue])\\n\"))), mdx(\"p\", null, mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useCallback\"), \" can be powerful if properly used, but it won't necessarily make a callback function better for performance. As you can see, it \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://5,7,8\"\n }, \"causes extra memory allocation\"), \" and the memoized one won't be garbage collected after the render, which is something you should be aware of. So remember to only \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useCallback\"), \" when you have to.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n })), mdx(\"h2\", null, \"Problem: Unnecessary Expensive Calculation\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function MyComponent({x, y}) {\\n return \u003cdiv\u003eHello World\u003c/div\u003e\\n}\\n\"))), mdx(\"p\", null, \"React comes with the concept of \\\"rendering\\\". When a component is being rendered by React, that means React is calling the component function (or the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"render\"), \" method in the case of class components). This comes with an unfortunate limitation that calculations performed within the function (or \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"render\"), \") will be performed every single render, regardless of whether the inputs for the calculations change. For example:\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function MyComponent({x, y}) {\\n const result = expensiveCalculation(x, y)\\n return \u003cdiv\u003eThe result is {result}.\u003c/div\u003e\\n}\\n\"))), mdx(\"p\", null, \"Every time the parent component of \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"MyComponent\"), \" is re-rendered or some state is added later and setting the state triggers re-renders, so for \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"expensiveCalculation\"), \", \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://2\"\n }, \"it will be recalculated in every render\"), \" of this component, which leads to performance bottlenecks.\"), mdx(CodeSlot, {\n style: {\
185n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"// how to optimize this with useMemo?\\nfunction MyComponent({x, y}) {\\n const result = expensiveCalculation(x, y)\\n return \u003cdiv\u003eThe result is {result}.\u003c/div\u003e\\n}\\n\"))), mdx(\"h3\", null, \"Solution: \", mdx(\"inlineCode\", {\n parentName: \"h3\"\n }, \"React.useMemo\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"const memoizedResult = React.useMemo(calculationCallback, dependencyList)\"), \": \", mdx(\"strong\", {\n parentName: \"p\"\n }, \"Memoizes the result of the calculation and only calls the calculation when the dependencies changes.\")), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function MyComponent({x, y}) {\\n const result = React.useMemo(() =\u003e expensiveCalculation(x, y), [x, y])\\n return \u003cdiv\u003eThe result is {result}.\u003c/div\u003e\\n}\\n\"))), mdx(\"p\", null, \"With \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://2:4\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"useMemo\")), \", here is the optimized version of \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"MyComponent\"), \". And now \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"result\"), \" will only be calculated again when \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"x\"), \" or \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"y\"), \" changes.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n })), mdx(\"h2\", null, \"So when should I \", mdx(\"inlineCode\", {\n parentName: \"h2\"\n }, \"useMemo\"), \" and \", mdx(\"inlineCode\", {\n parentName: \"h2\"\n }, \"useCallback\"), \"?\"), mdx(\"p\", null, \"Use \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useCallback\"), \" when you want to avoid unnecessary re-renders caused by \", mdx(\"strong\", {\n parentName: \"p\"\n }, \"referential inequality\"), \" of structural data types, for example:\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function Foo({bar, baz}) {\\n React.useEffect(() =\u003e {\\n const options = {bar, baz}\\n buzz(options)\\n }, [bar, baz])\\n return \u003cdiv\u003efoobar\u003c/div\u003e\\n}\\n\\nfunction Foo({bar, baz}) {\\n React.useEffect(() =\u003e {\\n const options = {bar, baz}\\n buzz(options)\\n }, [bar, baz])\\n return \u003cdiv\u003efoobar\u003c/div\u003e\\n}\\n\\nfunction Blub() {\\n const bar = React.useCallback(() =\u003e {}, [])\\n const baz = React.useMemo(() =\u003e [1, 2, 3], [])\\n return \u003cFoo bar={bar} baz={baz} /\u003e\\n}\\n\"))), mdx(\"p\", null, \"When using \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://18:21\"\n }, \"values\"), \" as \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://2,5,10,13\"\n }, \"dependencies\"), \" in \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useEffect\"), \".\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"const CountButton = React.memo(function CountButton({onClick, count}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n})\\n\\nfunction DualCounter() {\\n const [count1, setCount1] = React.useState(0)\\n const increment1 = React.useCallback(\\n () =\u003e setCount1((c) =\u003e c + 1),\\n []\\n )\\n\\n const [count2, setCount2] = React.useState(0)\\n const increment2 = React.useCallback(\\n () =\u003e setCount2((c) =\u003e c + 1),\\n []\\n )\\n\\n return (\\n \u003c\u003e\\n \u003cCountButton count={count1} onClick={increment1} /\u003e\\n \u003cCountButton count={count2} onClick={increment2} /\u003e\\n \u003c/\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"When using \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://1,9\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"React.memo\")), \" to avoid unnecessary child re-renders when you pass complex \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://14:17,21:24\"\n }, \"callbacks\"), \" as event handlers into child components. Because callback functions will be re-initialized every re-render.\"), mdx(CodeSlot, {\n style: {\
185n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function RenderPrimes({iterations, multiplier}) {\\n const primes = React.useMemo(\\n () =\u003e calculatePrimes(iterations, multiplier),\\n [iterations, multiplier]\\n )\\n return \u003cdiv\u003ePrimes! {primes}\u003c/div\u003e\\n}\\n\"))), mdx(\"p\", null, \"Use \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"useMemo\"), \" when you want to avoid unnecessary computationally expensive \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://5:9\"\n }, \"calculations\"), \".\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n })), mdx(\"h2\", null, \"Problem: Unnecessary Child Re-Renders\"), mdx(\"p\", null, \"Besides function re-initiation and value recalculation, there is another behavior that can lead to performance bottlenecks: \", mdx(\"strong\", {\n parentName: \"p\"\n }, \"unnecessary re-renders\"), \". As I said before, rendering means that React is calling your component function or its \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"render\"), \" method. React renders a component to recalculate the data to reflect the changes within the component. In what circumstances will a re-render be unnecessary?\"), mdx(\"p\", null, \"There are four reasons for React to re-render a component:\"), mdx(\"ol\", null, mdx(\"li\", {\n parentName: \"ol\"\n }, \"The \", mdx(\"strong\", {\n parentName: \"li\"\n }, \"state\"), \" of the component has changed, which must be triggered with the state setting function.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The \", mdx(\"strong\", {\n parentName: \"li\"\n }, \"props\"), \" the component receives have changed.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The \", mdx(\"strong\", {\n parentName: \"li\"\n }, \"context\"), \" values the component uses have changed.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The component's \", mdx(\"strong\", {\n parentName: \"li\"\n }, \"parent\"), \" has been re-rendered because of the reasons above.\")), mdx(\"p\", null, \"Now the first three can't and also shouldn't be avoided because those are basically data changes, which must be recalculated and displayed on the screen. But the last one is, most of the time, unnecessary. When a parent component changes, if there is no change to be applied on the child, there is no need to call the component function to reflect any data changes.\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"Here is an example:\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n
185const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"When you click the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://31:34\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"CountButton\")), \", it changes the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://23:26\"\n }, \"state\"), \" of its parent \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Example\"), \", which triggers a re-render for \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Example\"), \", which in turn leads to the unnecessary re-render of the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://37:40\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"NameInput\")), \" even though none of its props has changed.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"So how do we solve this problem?\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"h3\", null, \"Solution: \", mdx(\"inlineCode\", {\n parentName: \"h3\"\n }, \"React.memo\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo(component)\"), \": memoize a component and only re-render it when the props it receives changes.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\nNameInput = React.memo(NameInput)\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n
185const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"To prevent the unnecessary re-render of \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"NameInput\"), \", we can wrap it with \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" as a bail-out. Like \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://20\"\n }, \"this\"), \":\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(\"p\", null, \"By doing this we let React know that it doesn't need a re-render until at least one of its props changes.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\nCountButton = React.memo(CountButton)\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\nNameInput = React.memo(NameInput)\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n const [count, setCount] = React.useState(0)\\n const increment = () =\u003e {\\n setCount((c) =\u003e c + 1)\\n }\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"h4\", null, mdx(\"inlineCode\", {\n parentName: \"h4\"\n }, \"React.memo\"), \": Might not work like a charm\"), mdx(\"p\", null, \"You might want to ask what if we do this to the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"CountButton\"), \". Here is the answer: If we were to only wrap it into \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" like \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://6\"\n }, \"this\"), \", \", mdx(\"em\", {\n parentName: \"p\"\n }, \"it wouldn't work\"), \", because \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"increment\"), \" always gets re-initialized every time \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Example\"), \" gets re-rendered, which makes the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"onClick\"), \" prop of \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"CountButton\"), \" changes all the time due to referential inequality. But \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" only prevent re-renders when all the props stay the same.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function CountButton({count, onClick}) {\\n return \u003cbutton onClick={onClick}\u003e{count}\u003c/button\u003e\\n}\\nCountButton = React.memo(CountButton)\\n\\nfunction NameInput({name, onNameChange}) {\\n return (\\n \u003clabel\u003e\\n Name:{' '}\\n \u003cinput\\n value={name}\\n onChange={(e) =\u003e onNameChange(e.target.value)}\\n /\u003e\\n \u003c/label\u003e\\n )\\n}\\nNameInput = React.memo(NameInput)\\n\\nfunction Example() {\\n const [name, setName] = React.useState('')\\n
185const [count, setCount] = React.useState(0)\\n const increment = React.useCallback(() =\u003e setCount((c) =\u003e c + 1), [])\\n\\n return (\\n \u003cdiv\u003e\\n \u003cdiv\u003e\\n \u003cCountButton count={count} onClick={increment} /\u003e\\n \u003c/div\u003e\\n \u003cdiv\u003e\\n \u003cNameInput name={name} onNameChange={setName} /\u003e\\n \u003c/div\u003e\\n {name ? (\\n \u003cdiv\u003e{`${name}'s favorite number is ${count}`}\u003c/div\u003e\\n ) : null}\\n \u003c/div\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, mdx(\"strong\", {\n parentName: \"p\"\n }, \"Unless\"), \" we also wrap the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"increment\"), \" function within the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Example\"), \" with \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.useCallback\"), \". Like \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://26:29\"\n }, \"this\"), \":\"), mdx(\"p\", null, \"Just so you know that it's better to use \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" more mindfully rather than shove everything into it.\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n })), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function Menu({\\n items,\\n getMenuProps,\\n getItemProps,\\n highlightedIndex,\\n selectedItem,\\n}) {\\n return (\\n \u003cul {...getMenuProps()}\u003e\\n {items.map((item, index) =\u003e (\\n \u003cListItem\\n key={item.id}\\n getItemProps={getItemProps}\\n item={item}\\n index={index}\\n selectedItem={selectedItem}\\n highlightedIndex={highlightedIndex}\\n \u003e\\n {item.name}\\n \u003c/ListItem\u003e\\n ))}\\n \u003c/ul\u003e\\n )\\n}\\nMenu = React.memo(Menu)\\n\\nfunction ListItem({\\n getItemProps,\\n item,\\n index,\\n selectedItem,\\n highlightedIndex,\\n ...props\\n}) {\\n const isSelected = selectedItem?.id === item.id\\n const isHighlighted = highlightedIndex === index\\n return (\\n \u003cli\\n {...getItemProps({\\n index,\\n item,\\n style: {\\n fontWeight: isSelected ? 'bold' : 'normal',\\n backgroundColor: isHighlighted ? 'lightgray' : 'inherit',\\n },\\n ...props,\\n })}\\n /\u003e\\n )\\n}\\nListItem = React.memo(ListItem)\\n\"))), mdx(\"h4\", null, mdx(\"inlineCode\", {\n parentName: \"h4\"\n }, \"React.memo\"), \": Use a custom comparator function\"), mdx(\"p\", null, \"Most of the time, a simple \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" works just fine, there are times when its default behavior isn't what you desire.\"), mdx(\"p\", null, \"Imagine you are rendering a menu
185containing a list of items, and the user can highlight a item at a time. Your \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Menu\"), \" and \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"ListItem\"), \" components might look like \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://1,27\"\n }, \"this\"), \".\"), mdx(\"p\", null, \"Both of these components are wrapped into \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://25,57\"\n }, \"already\"), \". So now you might expect this way the performance has been optimized to its highest level. But here is a problem still unsolved: when the user highlights a different item, besides the the previously and newly highlighted items, all the other untouched items will still be re-rendered. And that's because every time the user highlights a different item, they changes the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"highlightedIndex\"), \" prop, which triggers the re-render of each item, no matter if it was highlighted previously or is highlighted now or never has been touched the whole time.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function Menu({\\n items,\\n getMenuProps,\\n getItemProps,\\n highlightedIndex,\\n selectedItem,\\n}) {\\n return (\\n \u003cul {...getMenuProps()}\u003e\\n {items.map((item, index) =\u003e (\\n \u003cListItem\\n key={item.id}\\n getItemProps={getItemProps}\\n item={item}\\n index={index}\\n selectedItem={selectedItem}\\n highlightedIndex={highlightedIndex}\\n \u003e\\n {item.name}\\n \u003c/ListItem\u003e\\n ))}\\n \u003c/ul\u003e\\n )\\n}\\nMenu = React.memo(Menu)\\n\\nfunction ListItem({\\n getItemProps,\\n item,\\n index,\\n selectedItem,\\n highlightedIndex,\\n ...props\\n}) {\\n const isSelected = selectedItem?.id === item.id\\n const isHighlighted = highlightedIndex === index\\n return (\\n \u003cli\\n {...getItemProps({\\n index,\\n item,\\n style: {\\n fontWeight: isSelected ? 'bold' : 'normal',\\n backgroundColor: isHighlighted ? 'lightgray' : 'inherit',\\n },\\n ...props,\\n })}\\n /\u003e\\n )\\n}\\nListItem = React.memo(ListItem, (prevProps, nextProps) =\u003e {\\n // true means do NOT rerender\\n // false means DO rerender\\n\\n // these ones are easy if any of these changed, we should re-render\\n if (prevProps.getItemProps !== nextProps.getItemProps) return false\\n if (prevProps.item !== nextProps.item) return false\\n if (prevProps.index !== nextProps.index) return false\\n if (prevProps.selectedItem !== nextProps.selectedItem) return false\\n\\n // this is trickier. We should only re-render if this list item:\\n // 1. was highlighted before and now it's not\\n // 2. was not highlighted before and now it is\\n if (prevProps.highlightedIndex !== nextProps.highlightedIndex) {\\n const wasPrevHighlighted =\\n prevProps.highlightedIndex === prevProps.index\\n const isNowHighlighted =\\n nextProps.highlightedIndex === nextProps.index\\n return wasPrevHighlighted === isNowHighlighted\\n }\\n return true\\n})\\n\"))), mdx(\"p\", null, \"To solve this problem, you'll need to change how React should compare props over time. And this is where the \", mdx(\"strong\", {\n parentName: \"p\"\n }, \"custom comparator\"), \" comes into play. You can pass a custom comparator as the second argument into \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"React.memo\"), \" so that it uses your, hopefully better, rules, instead of its default rules, for prop comparison. For example, for the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Menu\"), \" and \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"ListItem\"), \", we can do \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://59:98\"\n }, \"this\"), \":\"), mdx(\"p\", null, \"This way the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"ListItem\"), \" won't be re-rendered unless it is involved in highlight changes.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function Menu({\\n items,\\n getMenuProps,\\n getItemProps,\\n highlightedIndex,\\n selectedItem,\\n}) {\\n return (\\n \u003cul {...getMenuProps()}\u003e\\n {items.map((item, index) =\u003e (\\n \u003cListItem\\n key={item.id}\\n getItemProps={getItemProps}\\n item={item}\\n index={index}\\n // we move the calculation details here\\n isSelected={selectedItem?.
185id === item.id}\\n isHighlighted={highlightedIndex === index}\\n \u003e\\n {item.name}\\n \u003c/ListItem\u003e\\n ))}\\n \u003c/ul\u003e\\n )\\n}\\nMenu = React.memo(Menu)\\n\\nfunction ListItem({\\n getItemProps,\\n item,\\n index,\\n // we only pass two boolean props here\\n isHighlighted,\\n isSelected,\\n ...props\\n}) {\\n return (\\n \u003cli\\n {...getItemProps({\\n index,\\n item,\\n style: {\\n backgroundColor: isHighlighted ? 'lightgray' : 'inherit',\\n fontWeight: isSelected ? 'bold' : 'normal',\\n },\\n ...props,\\n })}\\n /\u003e\\n )\\n}\\n// and we don't need a custom comparator here\\nListItem = React.memo(ListItem)\\n\"))), mdx(\"h4\", null, mdx(\"inlineCode\", {\n parentName: \"h4\"\n }, \"React.memo\"), \": Parent keeps the calculation, children keep the primitive props\"), mdx(\"p\", null, \"You might notice the logic in the custom comparator above is quite mind-boggling, especially when you are working on a big project with a team of many people. So for that scenario, one takeaway you might find valuable is that, if you're rendering a ton of instances of a particular component, try to do calculations a little higher -- maybe in the parent or even higher -- so you only need to pass primitive values to the component and let those value changes trigger DOM updates. That way you won't have to worry about breaking memoization or create a custom comparator, which can save you quite some code and brain cells. For example, we can simplify the code above like \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://16:22,36:38,59:60\"\n }, \"this\"), \":\"), mdx(CodeSlot, {\n style: {\n zoom: 0.8\n },\n mdxType: \"CodeSlot\"\n }), mdx(\"p\", null, \"Notice how we move the details of the highlight and selection calculation up to the parent component \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Menu\"), \", only pass two boolean props \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"isHighlighted\"), \" and \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"isSelected\"), \" to the child component \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"ListItem\"), \" and don't even have to write a custom comparator function.\")), mdx(\"h2\", null, \"Problem: Rendering huge lists with large quantities of data\"), mdx(\"p\", null, \"As we learned before, React is pretty optimized itself and provides a suite of performance optimization tools for you to use. But if you were to make huge updates to the DOM, there is little React can do because there is just too much to do. This problem is always revealed by UIs like data visualization, grids, tables, and lists with lots of data. There\\u2019s only so much you can do before we have to conclude that we\\u2019re simply running too much code (or running the same small amount of code too many times).\"), mdx(\"h3\", null, \"Solution: Windowing\"), mdx(\"p\", null, \"But it's still possible for us to work around this: when the user scroll through a huge table, chances are that they are only gonna see a portion of it, and that portion they are viewing won't be bigger than their window size. So what we can do is just to fetch a tiny part of the whole data set and render only for that portion exposed to the user, and as the user scroll through the list, we just \\\"lazy\\\" fetch the additional data, render the newly needed UI, and destroy the unwanted part just in time. Because the \\\"lazy\\\" rendered part of the UI is no bigger than the user's window size, this technique is known as \\\"windowing\\\", aka \\\"virtualization\\\". This works perfectly for this particular problem and can save you a ton of computational power.\"), mdx(\"p\", null, mdx(\"img\", {\n parentName: \"p\",\n \"src\": \"https://user-images.githubusercontent.com/22409868/148178031-d8b785e6-e706-4f8f-9d24-2efc105e3ada.png\",\n \"alt\": \"Windowing a list\"\n })), mdx(\"p\", null, mdx(\"img\", {\n parentName: \"p\",\n \"src\": \"https://user-images.githubusercontent.com/22409868/148177888-9e3dadde-dd42-4e15-89a4-575fcb15d536.png\",\n \"alt\": \"Windowing a grid (table)\"\n })), mdx(\"p\", null, \"There are many libraries that allow you to use this \\\"windowing\\\" technique, such as \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://react-window.vercel.app/#/examples/list/fixed-size\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"react-window\")), \" and \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://bvaughn.github.io/react-virtualized/\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }
185, \"react-virtualized\")), \". These two are older compared to \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://react-virtual.tanstack.com/\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"react-virtual\")), \", which can be added to your project with one simple hook and supports all kinds of customization, including vertical, horizontal, grid, fixed, variable, dynamic, smooth and even infinite virtualization. Definitely give it a try!\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function MyListOfData({items}) {\\n return (\\n \u003cul style={{height: 300}}\u003e\\n {items.map((item) =\u003e (\\n \u003cli key={item.id}\u003e{item.name}\u003c/li\u003e\\n ))}\\n \u003c/ul\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"Here is a regular data list component.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"import {useVirtual} from 'react-virtual'\\n\\nfunction MyListOfData({items}) {\\n const listRef = React.useRef()\\n const rowVirtualizer = useVirtual({\\n size: items.length,\\n parentRef: listRef,\\n estimateSize: React.useCallback(() =\u003e 20, []),\\n overscan: 10,\\n })\\n\\n return (\\n \u003cul\\n ref={listRef}\\n style={{\\n position: 'relative',\\n height: 300,\\n }}\\n \u003e\\n \u003cli\\n style={{\\n height: rowVirtualizer.totalSize,\\n }}\\n /\u003e\\n {rowVirtualizer.virtualItems.map(({index, size, start}) =\u003e {\\n const item = items[index]\\n return (\\n \u003cli\\n key={item.id}\\n style={{\\n position: 'absolute',\\n top: 0,\\n left: 0,\\n width: '100%',\\n height: size,\\n transform: `translateY(${start}px)`,\\n }}\\n \u003e\\n {item.name}\\n \u003c/li\u003e\\n )\\n })}\\n \u003c/ul\u003e\\n )\\n}\\n\"))), mdx(\"p\", null, \"And here is the windowed version with \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"react-virtual\"), \".\")), mdx(\"h2\", null, \"Problem: Global state changes trigger slow batch re-renders\"), mdx(\"p\", null, \"Optimizing performance for a few components are relatively easy, compared to doing that for many different components when you use contexts or global state management tools, like Redux, to manage the state of your apps. This scenario doesn't give you a clear clue to fix the problem because none of the components are slow in isolation but they update slow together when there is a global state change. The root of the problem is that there are just too many components responding to the state update and many of them shouldn't even do that.\"), mdx(\"p\", null, \"One way to work around this is to \\\"colocate\\\" the global state down to each component, which works but can be quite some work for you if in a big project. So how can we make every component only re-render when the state it really cares about update?\"), mdx(\"h3\", null, \"Solution: Use Recoil\"), mdx(\"p\", null, mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://recoiljs.org/\"\n }, \"Recoil\"), \" allow you to connect to the exact piece of state, which is a huge performance benefit. Some people also suggest using \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://mobx.js.org/README.html\"\n }, \"MobX\"), \", and as the author of MobX Michel Weststrate \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://twitter.com/mweststrate/status/1261369870152871937\"\n }, \"tweeted\"), \", both Recoil and MobX solves the problem of efficient rendering widely shared state, the problem that \\\"React (Context), Redux and most state management libs don't solve.\\\" But I would recommend you to use Recoil with React. Because Recoil is built on top of React primitives, which makes it lean, flexible, and integrates closely with React's concurrent mode. It also handles data in an async way in a quasi-functional manner and integrates better with React hooks. In short, \", mdx(\"em\", {\n parentName: \"p\"\n }, \"Recoil thinks the way that React does\"), \". So going with Recoil won't be a bad decision for you if you want to manage complex state performantly with React.\"), mdx(\"h2\", null, \"Problem: Monitoring performance in production\"), mdx(\"p\", null, \"Things happen in production, especially for performance problems. There will be situations in which some unex
185pected mistake slips through the code review process and causes a performance problem. On the other hand, we can't make every user install the React devtool and profile the app for us as they use it. How are we gonna monitor things like that?\"), mdx(\"h3\", null, \"Solution: Use the \", mdx(\"inlineCode\", {\n parentName: \"h3\"\n }, \"\u003cReact.Profiler\u003e\")), mdx(\"p\", null, \"The React team has created an \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://reactjs.org/docs/profiler.html\"\n }, mdx(\"inlineCode\", {\n parentName: \"a\"\n }, \"\u003cReact.Profiler\u003e\")), \" API specifically for situation like this. It doesn\\u2019t give us quite as much information as the React DevTools do, but it does give us some useful information.\"), mdx(HikeWithNoPreview, {\n mdxType: \"HikeWithNoPreview\"\n }, mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"return (\\n \u003cApp\u003e\\n \u003cNavigation {...props} /\u003e\\n \u003c/App\u003e\\n)\\n\"))), mdx(\"p\", null, \"Here is a basic example:\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"return (\\n \u003cApp\u003e\\n \u003cProfiler id=\\\"Navigation\\\" onRender={onRenderCallback}\u003e\\n \u003cNavigation {...props} /\u003e\\n \u003c/Profiler\u003e\\n \u003c/App\u003e\\n)\\n\"))), mdx(\"p\", null, \"Wrap the target component within the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"Profiler\"), \" to enable this feature. It\\u2019s important to note that unless you build your app using \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"react-dom/profiling\"), \" and \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"scheduler/tracing-profiling\"), \" this component won\\u2019t do anything.\"), mdx(StepHead, {\n mdxType: \"StepHead\"\n }, mdx(\"pre\", null, mdx(\"code\", {\n parentName: \"pre\",\n \"className\": \"language-js\"\n }, \"function onRenderCallback(\\n id,\\n phase,\\n actualDuration,\\n baseDuration,\\n startTime,\\n commitTime,\\n interactions\\n) {\\n //\\n}\\n\\n// ...\\nreturn (\\n \u003cApp\u003e\\n \u003cProfiler id=\\\"Navigation\\\" onRender={onRenderCallback}\u003e\\n \u003cNavigation {...props} /\u003e\\n \u003c/Profiler\u003e\\n \u003c/App\u003e\\n)\\n\"))), mdx(\"p\", null, \"For the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"onRender\"), \" prop, you'll need to pass a callback function with a particular \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"focus://2:8\"\n }, \"signature\"), \" otherwise the profiler component won't work. The \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"id\"), \" prop of the Profiler tree that has just committed.\\nYou can trace the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"phase\"), \" of either \\\"mount\\\" (if the tree just mounted) or \\\"update\\\" (if it re-rendered. \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"actualDuration\"), \" is the time spent rendering the committed update. \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"baseDuration\"), \" is the estimated time to render the entire subtree without memoization. \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"startTime\"), \" is when React began rendering this update. \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"commitTime\"), \" is when React committed this update. \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"interactions\"), \" is the Set of interactions belonging to this update. In the callback you can do anything you want with the data, including do calculation, pass the result to your backend, aggregate or log render timings and so on.\")), mdx(\"p\", null, \"I might write about more things on React performance, such as devtools, React Query, browser performance profiler and so on. Stay tuned.\"));\n}\n\n;\nMDXContent.isMDXComponent = true;","renderedOutput":"\u003ch2\u003eProblem: Unnecessary Callback Re-Initiation\u003c/h2\u003e\u003cstyle class=\"ssmq-0\"\u003e.ssmq-0.ssmq-default{display:block}.ssmq-0:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-0.ssmq-min-width920px{display:block}.ssmq-0:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-0 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eWhen you\u0026#x27;re using a callback function within \u003ccode\u003euseEffect\u003c/code\u003e, it\u0026#x27;s hard to predict how callback function will be modified in the future. Only passing in certain variables from the callback to the deps list won\u0026#x27;t do. Because once the variables passed in gets removed, your dependencies won\u0026#x27;t work any more. But you still have to provide some values to the dependency list to sync \u003ccode\u003euseEffect\u003c/code\u003e with the callback change.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eOne way we
185can solve this is to include the entire callback function into the dependency list.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eBut doing this also introduces another problem -- because the callback function is defined in the component\u0026#x27;s function body, it will be re-initialized from scratch every time the component gets rendered, and since functions are compared through references, even if the code of a function isn\u0026#x27;t modified between renders, every time it gets re-initialized, it will be different from it \u0026quot;previous\u0026quot; self in the last render of the component, which will trigger a infinite re-rendering loop that you definitely wouldn\u0026#x27;t like. And that difference is what some people call \u0026quot;referential inequality\u0026quot;, which will also happen when you\u0026#x27;re working with objects and arrays, because these three types are structural types that are compared through references.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.useCallback\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003econst memoizedCallback = React.useCallback(callback, dependencyList)\u003c/code\u003e: \u003cstrong\u003eMemoizes a callback function, only re-initialized it when the dependencies change and avoids referential inequality.\u003c/strong\u003e\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eAnd now every time the component gets re-rendered, the \u003ccode\u003eupdateLocalStorage\u003c/code\u003e callback will only re-initialized when the \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003e\u003ccode\u003ecount\u003c/code\u003e\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e value changes, otherwise React will use the same copy of it.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003e\u003ccode\u003euseCallback\u003c/code\u003e can be powerful if properly used, but it won\u0026#x27;t necessarily make a callback function better for performance. As you can see, it \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ecauses extra memory allocation\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e and the memoized one won\u0026#x27;t be garbage collected after the render, which is something you should be aware of. So remember to only \u003ccode\u003euseCallback\u003c/code\u003e when you have to.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-0 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWhen you\u0026#x27;re using a callback function within \u003ccode\u003euseEffect\u003c/code\u003e, it\u0026#x27;s hard to predict how callback function will be modified in the future. Only passing in certain variables from the callback to the deps list won\u0026#x27;t do. Because once the variables passed in gets removed, your dependencies won\u0026#x27;t work any more. But you still have to provide some values to the dependency list to sync \u003ccode\u003euseEffect\u003c/code\u003e with the callback change.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eOne way we can solve this is to include the entire callback function into the dependency list.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003eupdateLocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token comment\"\u003e// \u0026lt;-- function as a dependency\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003eupdateLocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token comment\"\u003e// \u0026lt;-- function as a dependency\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eBut doing this also introduces another problem -- because the callback function is defined in the component\u0026#x27;s function body, it will be re-initialized from scratch every time the component gets rendered, and since functions are compared through references, even if the code of a function isn\u0026#x27;t modified between renders, every time it gets re-initialized, it will be different from it \u0026quot;previous\u0026quot; self in the last render of the component, which will trigger a infinite re-rendering loop that you definitely wouldn\u0026#x27;t like. And that difference is what some people call \u0026quot;referential inequality\u0026quot;, which will also happen when you\u0026#x27;re working with objects and arrays, because these three types are structural types that are compared through references.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003eupdateLocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token comment\"\u003e// changes in every render\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003eupdateLocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token comment\"\u003e// changes in every render\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.useCallback\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003econst memoizedCallback = React.useCallback(callback, dependencyList)\u003c/code\u003e: \u003cstrong\u003eMemoizes a callback function, only re-initialized it when the dependencies change and avoids referential inequality.\u003c/strong\u003e\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eAnd now every time the component gets re-rendered, the \u003ccode\u003eupdateLocalStorage\u003c/code\u003e callback will only re-initialized when the \u003ccode\u003ecount\u003c/code\u003e value changes, otherwise React will use the same copy of it.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e window\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003elocalStorage\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003esetItem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;count\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003e\u003ccode\u003euseCallback\u003c/code\u003e can be powerful if properly used, but it won\u0026#x27;t necessarily make a callback function better for performance. As you can see, it \u003c!-- --\u003ecauses extra memory allocation\u003c!-- --\u003e and the memoized one won\u0026#x27;t be garbage collected after the render, which is something you should be aware of. So remember to only \u003ccode\u003euseCallback\u003c/code\u003e when you have to.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e memoizedCallback \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseCallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ecallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003esomeValue\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e memoizedCallback \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseCallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ecallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003esomeValue\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-0\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-0\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1; i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003ch2\u003eProblem: Unnecessary Expensive Calculation\u003c/h2\u003e\u003cstyle class=\"ssmq-1\"\u003e.ssmq-1.ssmq-default{display:block}.ssmq-1:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-1.ssmq-min-width920px{display:block}.ssmq-1:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-1 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eReact comes with the concept of \u0026quot;rendering\u0026quot;. When a component is being rendered by React, that means React is calling the component function (or the \u003ccode\u003erender\u003c/code\u003e method in the case of class components). This comes with an unfortunate limitation that calculations performed within the function (or \u003ccode\u003erender\u003c/code\u003e) will be performed every single render, regardless of whether the inputs for the calculations change. For example:\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eEvery time the parent component of \u003ccode\u003eMyComponent\u003c/code\u003e is re-rendered or some state is added later and setting the state triggers re-renders, so for \u003ccode\u003eexpensiveCalculation\u003c/code\u003e, \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003eit will be recalculated in every render\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e of this component, which leads to performance bottlenecks.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.useMemo\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003econst memoizedResult = React.useMemo(calculationCallback, dependencyList)\u003c/code\u003e: \u003cstrong\u003eMemoizes the result of the calculation and only calls the calculation when the dependencies changes.\u003c/strong\u003e\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eWith \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003e\u003ccode\u003euseMemo\u003c/code\u003e\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e, here is the optimized version of \u003ccode\u003eMyComponent\u003c/code\u003e. And now \u003ccode\u003eresult\u003c/code\u003e will only be calculated again when \u003ccode\u003ex\u003c/code\u003e or \u003ccode\u003ey\u003c/code\u003e changes.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003ereturn\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eHello World\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003ereturn\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eHello World\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-1 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eReact comes with the concept of \u0026quot;rendering\u0026quot;. When a component is being rendered by React, that means React is calling the component function (or the \u003ccode\u003erender\u003c/code\u003e method in the case of class components). This comes with an unfortunate limitation that calculations performed within the function (or \u003ccode\u003erender\u003c/code\u003e) will be performed every single render, regardless of whether the inputs for the calculations change. For example:\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003ereturn\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eHello World\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003ereturn\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eHello World\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eEvery time the parent component of \u003ccode\u003eMyComponent\u003c/code\u003e is re-rendered or some state is added later and setting the state triggers re-renders, so for \u003ccode\u003eexpensiveCalculation\u003c/code\u003e, \u003c!-- --\u003eit will be recalculated in every render\u003c!-- --\u003e of this component, which leads to performance bottlenecks.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e result \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eexpensiveCalculation\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e result \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eexpensiveCalculation\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.useMemo\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003econst memoizedResult = React.useMemo(calculationCallback, dependencyList)\u003c/code\u003e: \u003cstrong\u003eMemoizes the result of the calculation and only calls the calculation when the dependencies changes.\u003c/strong\u003e\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWith \u003ccode\u003euseMemo\u003c/code\u003e, here is the optimized version of \u003ccode\u003eMyComponent\u003c/code\u003e. And now \u003ccode\u003eresult\u003c/code\u003e will only be calculated again when \u003ccode\u003ex\u003c/code\u003e or \u003ccode\u003ey\u003c/code\u003e changes.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e result \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eexpensiveCalculation\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e result \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eexpensiveCalculation\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token plain\"\u003ex\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e y\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-1\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-1\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1;
185 i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003ch2\u003eSo when should I \u003ccode\u003euseMemo\u003c/code\u003e and \u003ccode\u003euseCallback\u003c/code\u003e?\u003c/h2\u003e\u003cp\u003eUse \u003ccode\u003euseCallback\u003c/code\u003e when you want to avoid unnecessary re-renders caused by \u003cstrong\u003ereferential inequality\u003c/strong\u003e of structural data types, for example:\u003c/p\u003e\u003cstyle class=\"ssmq-2\"\u003e.ssmq-2.ssmq-default{display:block}.ssmq-2:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-2.ssmq-min-width920px{display:block}.ssmq-2:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-2 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eWhen using \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003evalues\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e as \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003edependencies\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e in \u003ccode\u003euseEffect\u003c/code\u003e.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eWhen using \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003e\u003ccode\u003eReact.memo\u003c/code\u003e\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e to avoid unnecessary child re-renders when you pass complex \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ecallbacks\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e as event handlers into child components. Because callback functions will be re-initialized every re-render.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eUse \u003ccode\u003euseMemo\u003c/code\u003e when you want to avoid unnecessary computationally expensive \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ecalculations\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e baz \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e2\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e3\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e baz \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e2\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e3\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-2 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWhen using \u003c!-- --\u003evalues\u003c!-- --\u003e as \u003c!-- --\u003edependencies\u003c!-- --\u003e in \u003ccode\u003euseEffect\u003c/code\u003e.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e baz \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e2\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e3\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e baz \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseMemo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e2\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e3\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWhen using \u003ccode\u003eReact.memo\u003c/code\u003e to avoid unnecessary child re-renders when you pass complex \u003c!-- --\u003ecallbacks\u003c!-- --\u003e as event handlers into child components. Because callback functions will be re-initialized every re-render.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e CountButton \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003ememo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token keyword\"\u003efunction\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eCountButton\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token parameter\"\u003eonClick\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token parameter\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e CountButton \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003ememo\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token keyword\"\u003efunction\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003eCountButton\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token parameter\"\u003eonClick\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token parameter\"\u003e count\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eUse \u003ccode\u003euseMemo\u003c/code\u003e when you want to avoid unnecessary computationally expensive \u003c!-- --\u003ecalculations\u003c!-- --\u003e.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003ecalculatePrimes\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003eiterations\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e multiplier\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003ecalculatePrimes\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token plain\"\u003eiterations\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e multiplier\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-2\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-2\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1; i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003ch2\u003eProblem: Unnecessary Child Re-Renders\u003c/h2\u003e\u003cp\u003eBesides function re-initiation and value recalculation, there is another behavior that can lead to performance bottlenecks: \u003cstrong\u003eunnecessary re-renders\u003c/strong\u003e. As I said before, rendering means that React is calling your component function or its \u003ccode\u003erender\u003c/code\u003e method. React renders a component to recalculate the data to reflect the changes within the component. In what circumstances will a re-render be unnecessary?\u003c/p\u003e\u003cp\u003eThere are four reasons for React to re-render a component:\u003c/p\u003e\u003col\u003e\u003cli\u003eThe \u003cstrong\u003estate\u003c/strong\u003e of the component has changed, which must be triggered with the state setting function.\u003c/li\u003e\u003cli\u003eThe \u003cstrong\u003eprops\u003c/strong\u003e the component receives have changed.\u003c/li\u003e\u003cli\u003eThe \u003cstrong\u003econtext\u003c/strong\u003e values the component uses have changed.\u003c/li\u003e\u003cli\u003eThe component\u0026#x27;s \u003cstrong\u003eparent\u003c/strong\u003e has been re-rendered because of the reasons above.\u003c/li\u003e\u003c/ol\u003e\u003cp\u003eNow the first three can\u0026#x27;t and also shouldn\u0026#x27;t be avoided because those are basically data changes, which must be recalculated and displayed on the screen. But the last one is, most of the time, unnecessary. When a parent component changes, if there is no change to be applied on the child, there is no need to call the component function to reflect any data changes.\u003c/p\u003e\u003cstyle class=\"ssmq-3\"\u003e.ssmq-3.ssmq-default{display:block}.ssmq-3:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-3.ssmq-min-width920px{display:block}.ssmq-3:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-3 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eHere is an example:\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eWhen you click the \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003e\u003ccode\u003eCountButton\u003c/code\u003e\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e, it changes the \u
185003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003estate\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e of its parent \u003ccode\u003eExample\u003c/code\u003e, which triggers a re-render for \u003ccode\u003eExample\u003c/code\u003e, which in turn leads to the unnecessary re-render of the \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003e\u003ccode\u003eNameInput\u003c/code\u003e\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e even though none of its props has changed.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eSo how do we solve this problem?\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.memo\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003eReact.memo(component)\u003c/code\u003e: memoize a component and only re-render it when the props it receives changes.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eTo prevent the unnecessary re-render of \u003ccode\u003eNameInput\u003c/code\u003e, we can wrap it with \u003ccode\u003eReact.memo\u003c/code\u003e as a bail-out. Like \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e:\u003c/p\u003e\u003cp\u003eBy doing this we let React know that it doesn\u0026#x27;t need a re-render until at least one of its props changes.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Might not work like a charm\u003c/h4\u003e\u003cp\u003eYou might want to ask what if we do this to the \u003ccode\u003eCountButton\u003c/code\u003e. Here is the answer: If we were to only wrap it into \u003ccode\u003eReact.memo\u003c/code\u003e like \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e, \u003cem\u003eit wouldn\u0026#x27;t work\u003c/em\u003e, because \u003ccode\u003eincrement\u003c/code\u003e al
185ways gets re-initialized every time \u003ccode\u003eExample\u003c/code\u003e gets re-rendered, which makes the \u003ccode\u003eonClick\u003c/code\u003e prop of \u003ccode\u003eCountButton\u003c/code\u003e changes all the time due to referential inequality. But \u003ccode\u003eReact.memo\u003c/code\u003e only prevent re-renders when all the props stay the same.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003e\u003cstrong\u003eUnless\u003c/strong\u003e we also wrap the \u003ccode\u003eincrement\u003c/code\u003e function within the \u003ccode\u003eExample\u003c/code\u003e with \u003ccode\u003eReact.useCallback\u003c/code\u003e. Like \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e:\u003c/p\u003e\u003cp\u003eJust so you know that it\u0026#x27;s better to use \u003ccode\u003eReact.memo\u003c/code\u003e more mindfully rather than shove everything into it.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-3 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eHere is an example:\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWhen you click the \u003ccode\u003eCountButton\u003c/code\u003e, it changes the \u003c!-- --\u003estate\u003c!-- --\u003e of its parent \u003ccode\u003eExample\u003c/code\u003e, which triggers a re-render for \u003ccode\u003eExample\u003c/code\u003e, which in turn leads to the unnecessary re-render of the \u003ccode\u003eNameInput\u003c/code\u003e even though none of its props has changed.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eSo how do we solve this problem?\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch3\u003eSolution: \u003ccode\u003eReact.memo\u003c/code\u003e\u003c/h3\u003e\u003cp\u003e\u003ccode\u003eReact.memo(component)\u003c/code\u003e: memoize a component and only re-render it when the props it receives changes.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eTo prevent the unnecessary re-render of \u003ccode\u003eNameInput\u003c/code\u003e, we can wrap it with \u003ccode\u003eReact.memo\u003c/code\u003e as a bail-out. Like \u003c!-- --\u003ethis\u003c!-- --\u003e:\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cp\u003eBy doing this we let React know that it doesn\u0026#x27;t need a re-render until at least one of its props changes.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Might not work like a charm\u003c/h4\u003e\u003cp\u003eYou might want to ask what if we do this to the \u003ccode\u003eCountButton\u003c/code\u003e. Here is the answer: If we were to only wrap it into \u003ccode\u003eReact.memo\u003c/code\u003e like \u003c!-- --\u003ethis\u003c!-- --\u003e, \u003cem\u003eit wouldn\u0026#x27;t work\u003c/em\u003e, because \u003ccode\u003eincrement\u003c/code\u003e always gets re-initialized every time \u003ccode\u003eExample\u003c/code\u003e gets re-rendered, which makes the \u003ccode\u003eonClick\u003c/code\u003e prop of \u003ccode\u003eCountButton\u003c/code\u003e changes all the time due to referential inequality. But \u003ccode\u003eReact.memo\u003c/code\u003e only prevent re-renders when all the props stay the same.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ename\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;s favorite number is \u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e${\u003c/span\u003e\u003cspan class=\"token interpolation\"\u003ecount\u003c/span\u003e\u003cspan class=\"token interpolation-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token template-punctuation\"\u003e`\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003ediv\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003e\u003cstrong\u003eUnless\u003c/strong\u003e we also wrap the \u003ccode\u003eincrement\u003c/code\u003e function within the \u003ccode\u003eExample\u003c/code\u003e with \u003ccode\u003eReact.useCallback\u003c/code\u003e. Like \u003c!-- --\u003ethis\u003c!-- --\u003e:\u003c/p\u003e\u003cp\u003eJust so you know that it\u0026#x27;s better to use \u003ccode\u003eReact.memo\u003c/code\u003e more mindfully rather than shove everything into it.\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e increment \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseCallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003esetCount\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token parameter\"\u003ec\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e c \u003c/span\u003e\u003cspan class=\"token operator\"\u003e+\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token keyword\"\u003econst\u003c/span\u003e\u003cspan class=\"token plain\"\u003e increment \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token plain\"\u003e React\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token function\"\u003euseCallback\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token function\"\u003esetCount\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e(\u003c/span\u003e\u003cspan class=\"token parameter\"\u003ec\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u0026gt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e c \u003c/span\u003e\u003cspan class=\"token operator\"\u003e+\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token number\"\u003e1\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e)\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-3\"\u003e(function() {\
185n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-3\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1; i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003cstyle class=\"ssmq-4\"\u003e.ssmq-4.ssmq-default{display:block}.ssmq-4:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-4.ssmq-min-width920px{display:block}.ssmq-4:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-4 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Use a custom comparator function\u003c/h4\u003e\u003cp\u003eMost of the time, a simple \u003ccode\u003eReact.memo\u003c/code\u003e works just fine, there are times when its default behavior isn\u0026#x27;t what you desire.\u003c/p\u003e\u003cp\u003eImagine you are rendering a menu
185containing a list of items, and the user can highlight a item at a time. Your \u003ccode\u003eMenu\u003c/code\u003e and \u003ccode\u003eListItem\u003c/code\u003e components might look like \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e.\u003c/p\u003e\u003cp\u003eBoth of these components are wrapped into \u003ccode\u003eReact.memo\u003c/code\u003e \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ealready\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e. So now you might expect this way the performance has been optimized to its highest level. But here is a problem still unsolved: when the user highlights a different item, besides the the previously and newly highlighted items, all the other untouched items will still be re-rendered. And that\u0026#x27;s because every time the user highlights a different item, they changes the \u003ccode\u003ehighlightedIndex\u003c/code\u003e prop, which triggers the re-render of each item, no matter if it was highlighted previously or is highlighted now or never has been touched the whole time.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eTo solve this problem, you\u0026#x27;ll need to change how React should compare props over time. And this is where the \u003cstrong\u003ecustom comparator\u003c/strong\u003e comes into play. You can pass a custom comparator as the second argument into \u003ccode\u003eReact.memo\u003c/code\u003e so that it uses your, hopefully better, rules, instead of its default rules, for prop comparison. For example, for the \u003ccode\u003eMenu\u003c/code\u003e and \u003ccode\u003eListItem\u003c/code\u003e, we can do \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e:\u003c/p\u003e\u003cp\u003eThis way the \u003ccode\u003eListItem\u003c/code\u003e won\u0026#x27;t be re-rendered unless it is involved in highlight changes.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Parent keeps the calculation, children keep the primitive props\u003c/h4\u003e\u003cp\u003eYou might notice the logic in the custom comparator above is quite mind-boggling, especially when you are working on a big project with a team of many people. So for that scenario, one takeaway you might find valuable is that, if you\u0026#x27;re rendering a ton of instances of a particular component, try to do calculations a little higher -- maybe in the parent or even higher -- so you only need to pass primitive values to the component and let those value changes trigger DOM updates. That way you won\u0026#x27;t have to worry about breaking memoization or create a custom comparator, which can save you quite some code and brain cells. For example, we can simplify the code above like \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003ethis\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e:\u003c/p\u003e\u003cp\u003eNotice how we move the details of the highlight and selection calculation up to the parent component \u003ccode\u003eMenu\u003c/code\u003e, only pass two boolean props \u003ccode\u003eisHighlighted\u003c/code\u003e and \u003ccode\u003eisSelected\u003c/code\u003e to the child component \u003ccode\u003eListItem\u003c/code\u003e and don\u0026#x27;t even have to write a custom comparator function.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token literal-property\"\u003ebackgroundColor\u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e isHighlighted \u003c/span\u003e\u003cspan class=\"token operator\"\u003e?\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;lightgray\u0026#x27;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;inherit\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token literal-property\"\u003ebackgroundColor\u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e isHighlighted \u003c/span\u003e\u003cspan class=\"token operator\"\u003e?\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;lightgray\u0026#x27;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;inherit\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-4 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Use a custom comparator function\u003c/h4\u003e\u003cp\u003eMost of the time, a simple \u003ccode\u003eReact.memo\u003c/code\u003e works just fine, there are times when its default behavior isn\u0026#x27;t what you desire.\u003c/p\u003e\u003cp\u003eImagine you are rendering a menu
185containing a list of items, and the user can highlight a item at a time. Your \u003ccode\u003eMenu\u003c/code\u003e and \u003ccode\u003eListItem\u003c/code\u003e components might look like \u003c!-- --\u003ethis\u003c!-- --\u003e.\u003c/p\u003e\u003cp\u003eBoth of these components are wrapped into \u003ccode\u003eReact.memo\u003c/code\u003e \u003c!-- --\u003ealready\u003c!-- --\u003e. So now you might expect this way the performance has been optimized to its highest level. But here is a problem still unsolved: when the user highlights a different item, besides the the previously and newly highlighted items, all the other untouched items will still be re-rendered. And that\u0026#x27;s because every time the user highlights a different item, they changes the \u003ccode\u003ehighlightedIndex\u003c/code\u003e prop, which triggers the re-render of each item, no matter if it was highlighted previously or is highlighted now or never has been touched the whole time.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eTo solve this problem, you\u0026#x27;ll need to change how React should compare props over time. And this is where the \u003cstrong\u003ecustom comparator\u003c/strong\u003e comes into play. You can pass a custom comparator as the second argument into \u003ccode\u003eReact.memo\u003c/code\u003e so that it uses your, hopefully better, rules, instead of its default rules, for prop comparison. For example, for the \u003ccode\u003eMenu\u003c/code\u003e and \u003ccode\u003eListItem\u003c/code\u003e, we can do \u003c!-- --\u003ethis\u003c!-- --\u003e:\u003c/p\u003e\u003cp\u003eThis way the \u003ccode\u003eListItem\u003c/code\u003e won\u0026#x27;t be re-rendered unless it is involved in highlight changes.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003ch4\u003e\u003ccode\u003eReact.memo\u003c/code\u003e: Parent keeps the calculation, children keep the primitive props\u003c/h4\u003e\u003cp\u003eYou might notice the logic in the custom comparator above is quite mind-boggling, especially when you are working on a big project with a team of many people. So for that scenario, one takeaway you might find valuable is that, if you\u0026#x27;re rendering a ton of instances of a particular component, try to do calculations a little higher -- maybe in the parent or even higher -- so you only need to pass primitive values to the component and let those value changes trigger DOM updates. That way you won\u0026#x27;t have to worry about breaking memoization or create a custom comparator, which can save you quite some code and brain cells. For example, we can simplify the code above like \u003c!-- --\u003ethis\u003c!-- --\u003e:\u003c/p\u003e\u003cdiv class=\"ch-hike-step-code\" style=\"zoom:0.8\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token literal-property\"\u003ebackgroundColor\u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e isHighlighted \u003c/span\u003e\u003cspan class=\"token operator\"\u003e?\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;lightgray\u0026#x27;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;inherit\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token literal-property\"\u003ebackgroundColor\u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e isHighlighted \u003c/span\u003e\u003cspan class=\"token operator\"\u003e?\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;lightgray\u0026#x27;\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e:\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token string\"\u003e\u0026#x27;inherit\u0026#x27;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e,\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003cp\u003eNotice how we move the details of the highlight and selection calculation up to the parent component \u003ccode\u003eMenu\u003c/code\u003e, only pass two boolean props \u003ccode\u003eisHighlighted\u003c/code\u003e and \u003ccode\u003eisSelected\u003c/code\u003e to the child component \u003ccode\u003eListItem\u003c/code\u003e and don\u0026#x27;t even have to write a custom comparator function.\u003c/p\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-4\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-4\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1; i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003ch2\u003eProblem: Rendering huge lists with large quantities of data\u003c/h2\u003e\u003cp\u003eAs we learned before, React is pretty optimized itself and provides a suite of performance optimization tools for you to use. But if you were to make huge updates to the DOM, there is little React can do because there is just too much to do. This problem is always revealed by UIs like data visualization, grids, tables, and lists with lots of data. Thereâs only so much you can do before we have to conclude that weâre simply running too much code (or running the same small amount of code too many times).\u003c/p\u003e\u003ch3\u003eSolution: Windowing\u003c/h3\u003e\u003cp\u003eBut it\u0026#x27;s still possible for us to work around this: when the user scroll through a huge table, chances are that they are only gonna see a portion of it, and that portion they are viewing won\u0026#x27;t be bigger than their window size. So what we can do is just to fetch a tiny part of the whole data set and render only for that portion exposed to the user, and as the user scroll through the list, we just \u0026quot;lazy\u0026quot; fetch the additional data, render the newly needed UI, and destroy the unwanted part just in time. Because the \u0026quot;lazy\u0026quot; rendered part of the UI is no bigger than the user\u0026#x27;s window size, this technique is known as \u0026quot;windowing\u0026quot;, aka \u0026quot;virtualization\u0026quot;. This works perfectly for this particular problem and can save you a ton of computational power.\u003c/p\u003e\u003cp\u003e\u003cimg src=\"https://user-images.githubusercontent.com/22409868/148178031-d8b785e6-e706-4f8f-9d24-2efc105e3ada.png\" alt=\"Windowing a list\"/\u003e\u003c/p\u003e\u003cp\u003e\u003cimg src=\"https://user-images.githubusercontent.com/22409868/148177888-9e3dadde-dd42-4e15-89a4-575fcb15d536.png\" alt=\"Windowing a grid (table)\"/\u003e\u003c/p\u003e\u003cp\u003eThere are many libraries that allow you to use this \u0026quot;windowing\u0026quot; technique, such as \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://react-window.vercel.app/#/examples/list/fixed-size\" class=\"text-blue-700\"\u003e\u003ccode\u003ereact-window\u003c/code\u003e\u003c/a\u003e and \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://bvaughn.github.io/react-virtualized/\" class=\"text-blue-700\"\u003e\u003ccode\u003ereact-virtualized\u003c/code\u003e\u003c/a\u003e. These two are older compared to \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://react-virtual.tanstack.com/\" class=\"text-blue-700\"\u003e\u003ccode\u003ereact-virtual\u003c/code\u003e\u003c/a\u003e, which can be added to your project with one simple hook and supports all kinds of customization, including vertical, horizontal, grid, fixed, variable, dynamic, smooth and even infinite virtualization. Definitely give it a try!\u003c/p\u003e\u003cstyle class=\"ssmq-5\"\u003e.ssmq-5.ssmq-default{display:block}.ssmq-5:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-5.ssmq-min-width920px{display:block}.ssmq-5:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-5 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eHere is a regular data list component.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eAnd here is the windowed version with \u003ccode\u003ereact-virtual\u003c/code\u003e.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eli key\u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token plain\"\u003eitem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003eid\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token plain\"\u003eitem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003ename\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003eli\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eli key\u003c/span\u003e\u003cspan class=\"token operator\"\u003e=\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token plain\"\u003eitem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003eid\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token plain\"\u003eitem\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e.\u003c/span\u003e\u003cspan class=\"token plain\"\u003ename\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token plain\"\u003eli\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-5 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eHere is a regular data list component.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eAnd here is the windowed version with \u003ccode\u003ereact-virtual\u003c/code\u003e.\u003c/p\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-5\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-5\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1; i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEac
185h(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003ch2\u003eProblem: Global state changes trigger slow batch re-renders\u003c/h2\u003e\u003cp\u003eOptimizing performance for a few components are relatively easy, compared to doing that for many different components when you use contexts or global state management tools, like Redux, to manage the state of your apps. This scenario doesn\u0026#x27;t give you a clear clue to fix the problem because none of the components are slow in isolation but they update slow together when there is a global state change. The root of the problem is that there are just too many components responding to the state update and many of them shouldn\u0026#x27;t even do that.\u003c/p\u003e\u003cp\u003eOne way to work around this is to \u0026quot;colocate\u0026quot; the global state down to each component, which works but can be quite some work for you if in a big project. So how can we make every component only re-render when the state it really cares about update?\u003c/p\u003e\u003ch3\u003eSolution: Use Recoil\u003c/h3\u003e\u003cp\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://recoiljs.org/\" class=\"text-blue-700\"\u003eRecoil\u003c/a\u003e allow you to connect to the exact piece of state, which is a huge performance benefit. Some people also suggest using \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://mobx.js.org/README.html\" class=\"text-blue-700\"\u003eMobX\u003c/a\u003e, and as the author of MobX Michel Weststrate \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://twitter.com/mweststrate/status/1261369870152871937\" class=\"text-blue-700\"\u003etweeted\u003c/a\u003e, both Recoil and MobX solves the problem of efficient rendering widely shared state, the problem that \u0026quot;React (Context), Redux and most state management libs don\u0026#x27;t solve.\u0026quot; But I would recommend you to use Recoil with React. Because Recoil is built on top of React primitives, which makes it lean, flexible, and integrates closely with React\u0026#x27;s concurrent mode. It also handles data in an async way in a quasi-functional manner and integrates better with React hooks. In short, \u003cem\u003eRecoil thinks the way that React does\u003c/em\u003e. So going with Recoil won\u0026#x27;t be a bad decision for you if you want to manage complex state performantly with React.\u003c/p\u003e\u003ch2\u003eProblem: Monitoring performance in production\u003c/h2\u003e\u003cp\u003eThings happen in production, especially for performance problems. There will be situations in which some unexpected mistake slips through the code review process and causes a performance problem. On the other hand, we can\u0026#x27;t make every user install the React devtool and profile the app for us as they use it. How are we gonna monitor things like that?\u003c/p\u003e\u003ch3\u003eSolution: Use the \u003ccode\u003e\u0026lt;React.Profiler\u0026gt;\u003c/code\u003e\u003c/h3\u003e\u003cp\u003eThe React team has created an \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://reactjs.org/docs/profiler.html\" class=\"text-blue-700\"\u003e\u003ccode\u003e\u0026lt;React.Profiler\u0026gt;\u003c/code\u003e\u003c/a\u003e API specifically for situation like this. It doesnât give us quite as much information as the React DevTools do, but it does give us some useful information.\u003c/p\u003e\u003cstyle class=\"ssmq-6\"\u003e.ssmq-6.ssmq-default{display:block}.ssmq-6:not(.ssmq-default){display: none;}\n@media (min-width: 920px){.ssmq-6.ssmq-min-width920px{display:block}.ssmq-6:not(.ssmq-min-width920px){display: none;}}\u003c/style\u003e\u003cdiv class=\"ssmq-6 ssmq-min-width920px\"\u003e\u003csection class=\"ch-hike ch-hike-fluid\"\u003e\u003cdiv class=\"ch-hike-content\"\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-focused\"\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-focused border-none\"\u003e\u003cp\u003eHere is a basic example:\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eWrap the target component within the \u003ccode\u003eProfiler\u003c/code\u003e to enable this feature. Itâs important to note that unless you build your app using \u003ccode\u003ereact-dom/profiling\u003c/code\u003e and \u003ccode\u003escheduler/tracing-profiling\u003c/code\u003e this component wonât do anything.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step ch-hike-step-unfocused\"\u003e\u003cdiv class=\"ch-hike-step-gap\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step-content ch-hike-step-content-unfocused border-none opacity-25 transition-opacity\"\u003e\u003cp\u003eFor the \u003ccode\u003eonRender\u003c/code\u003e prop, you\u0026#x27;ll need to pass a callback function with a particular \u003cbutton class=\"ch-hike-focus ch-hike-focus-inactive\" title=\"Show code\"\u003esignature\u003csvg fill=\"none\" stroke=\"currentColor\" class=\"ch-hike-focus-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/button\u003e otherwise the profiler component won\u0026#x27;t work. The \u003ccode\u003eid\u003c/code\u003e prop of the Profiler tree that has just committed.\nYou can trace the \u003ccode\u003ephase\u003c/code\u003e of either \u0026quot;mount\u0026quot; (if the tree just mounted) or \u0026quot;update\u0026quot; (if it re-rendered. \u003ccode\u003eactualDuration\u003c/code\u003e is the time spent rendering the committed update. \u
185003ccode\u003ebaseDuration\u003c/code\u003e is the estimated time to render the entire subtree without memoization. \u003ccode\u003estartTime\u003c/code\u003e is when React began rendering this update. \u003ccode\u003ecommitTime\u003c/code\u003e is when React committed this update. \u003ccode\u003einteractions\u003c/code\u003e is the Set of interactions belonging to this update. In the callback you can do anything you want with the data, including do calculation, pass the result to your backend, aggregate or log render timings and so on.\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003caside class=\"ch-hike-sticker-column\"\u003e\u003cdiv class=\"ch-hike-sticker\"\u003e\u003cdiv class=\"ch-hike-editor\"\u003e\u003cdiv style=\"height:100%\" class=\"ch-editor-frame undefined\"\u003e\u003cdiv class=\"ch-frame\"\u003e\u003cdiv class=\"ch-frame-title-bar bg-editor-bg\"\u003e\u003cdiv class=\"ch-frame-buttons\"\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-left bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-middle bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button-space\"\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-button ch-frame-button-right bg-button-bg border-button-bg\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv title=\"App.js\" data-ch-tab=\"north\" class=\"ch-editor-tab ch-editor-tab-active border-none bg-editor-bg\"\u003e\u003cdiv\u003eApp.js\u003c/div\u003e\u003c/div\u003e\u003cdiv style=\"flex:1\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"ch-frame-content\"\u003e\u003cdiv class=\"ch-frame-zoom\" style=\"--ch-frame-zoom:1;
185overflow:unset\"\u003e\u003cdiv data-ch-panel=\"north\" class=\"ch-editor-body\" style=\"height:100%\"\u003e\u003cpre class=\"language-js\" style=\"opacity:0;overflow:hidden\"\u003e\u003ccode\u003e\u003cdiv class=\"prev-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eNavigation \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token operator\"\u003e...\u003c/span\u003e\u003cspan class=\"token plain\"\u003eprops\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003cdiv class=\"next-longest-line\"\u003e\u003cdiv style=\"display:inline-block\"\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"token plain\"\u003eNavigation \u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"token operator\"\u003e...\u003c/span\u003e\u003cspan class=\"token plain\"\u003eprops\u003c/span\u003e\u003cspan class=\"token punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"token plain\"\u003e \u003c/span\u003e\u003cspan class=\"token operator\"\u003e/\u003c/span\u003e\u003cspan class=\"token operator\"\u003e\u0026gt;\u003c/span\u003e\u003cbr/\u003e\u003c/div\u003e\u003c/div\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/aside\u003e\u003c/section\u003e\u003c/div\u003e\u003cdiv class=\"ssmq-6 ssmq-default\"\u003e\u003csection class=\"ch-hike ch-hike-fixed\"\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eHere is a basic example:\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eWrap the target component within the \u003ccode\u003eProfiler\u003c/code\u003e to enable this feature. Itâs important to note that unless you build your app using \u003ccode\u003ereact-dom/profiling\u003c/code\u003e and \u003ccode\u003escheduler/tracing-profiling\u003c/code\u003e this component wonât do anything.\u003c/p\u003e\u003c/div\u003e\u003cdiv class=\"ch-hike-step\"\u003e\u003cp\u003eFor the \u003ccode\u003eonRender\u003c/code\u003e prop, you\u0026#x27;ll need to pass a callback function with a particular \u003c!-- --\u003esignature\u003c!-- --\u003e otherwise the profiler component won\u0026#x27;t work. The \u003ccode\u003eid\u003c/code\u003e prop of the Profiler tree that has just committed.\nYou can trace the \u003ccode\u003ephase\u003c/code\u003e of either \u0026quot;mount\u0026quot; (if the tree just mounted) or \u0026quot;update\u0026quot; (if it re-rendered. \u003ccode\u003eactualDuration\u003c/code\u003e is the time spent rendering the committed update. \u003ccode\u003ebaseDuration\u003c/code\u003e is the estimated time to render the entire subtree without memoization. \u003ccode\u003estartTime\u003c/code\u003e is when React began rendering this update. \u003ccode\u003ecommitTime\u003c/code\u003e is when React committed this update. \u003ccode\u003einteractions\u003c/code\u003e is the Set of interactions belonging to this update. In the callback you can do anything you want with the data, including do calculation, pass the result to your backend, aggregate or log render timings and so on.\u003c/p\u003e\u003c/div\u003e\u003c/section\u003e\u003c/div\u003e\u003cscript class=\"ssmq-6\"\u003e(function() {\n var qs = [\"(min-width: 920px)\",\"default\"];\n var clss = [\"ssmq-min-width920px\",\"ssmq-default\"];\n var mainCls = \"ssmq-6\";\n\n var scrEls = document.getElementsByTagName(\"script\");\n var scrEl = scrEls[scrEls.length - 1];\n var parent = scrEl.parentNode;\n\n var el = null;\n for (var i = 0; i \u003c qs.length - 1;
185 i++) {\n if (window.matchMedia(qs[i]).matches) {\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + clss[i]);\n break;\n }\n }\n if (!el) {\n var defaultClass = clss.pop();\n el = parent.querySelector(\":scope \u003e .\" + mainCls + \".\" + defaultClass);\n }\n el.removeAttribute(\"class\");\n\n parent.querySelectorAll(\":scope \u003e .\" + mainCls).forEach(function (e) {\n parent.removeChild(e);\n });\n})();\u003c/script\u003e\u003cp\u003eI might write about more things on React performance, such as devtools, React Query, browser performance profiler and so on. Stay tuned.\u003c/p\u003e","scope":{"title":"React Performance Recipes","excerpt":"Performance optimization can be daunting, but there are still more solutions than problems. In this post, I talked about how to tackle common performance issues when developing with React. Get a solution for every React performance problem you might encounter.","coverImage":"/assets/blog/react-po/cover.jpg","date":"2022-01-05"}},"frontMatter":{"title":"React Performance Recipes","excerpt":"Performance optimization can be daunting, but there are still more solutions than problems. In this post, I talked about how to tackle common performance issues when developing with React. Get a solution for every React performance problem you might encounter.","coverImage":"/assets/blog/react-po/cover.jpg","date":"2022-01-05"}},"__N_SSG":true},"page":"/posts/[slug]","query":{"slug":"react-performance-recipes"},"buildId":"nRYnRkVXs0kRjiu1HTnAF","isFallback":false,"gsp":true,"scriptLoader":[]}</script>
185</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.