{"data":{"site":{"siteMetadata":{"title":"This web dev stuff is hard","author":"Paul Armstrong"}},"markdownRemark":{"id":"8b13b802-6249-5d29-a7be-667836045d3b","excerpt":"Rendering large trees of components in React can be expensive. When the state managed by Redux changes, it triggers updates to the main component tree and on…","html":"<p>Rendering large trees of components in React can be expensive. When the state managed by Redux changes, it triggers updates to the main component tree and on down through all of the sub-trees. When state updates often, this can start to slow down your application while it needs to re-render every component.</p>\n<p>To prevent this from happening, you might make use of <code class=\"language-text\">PureComponent</code> and <code class=\"language-text\">shouldComponentUpdate</code>. While each of these is a strategy for managing updates to components and their sub-trees, there are certain patterns that can slip by and not be caught appropriately.</p>\n<p>It is also important to understand that using <code class=\"language-text\">PureComponent</code> is typically the goal, whenever possible, since it is optimized at React’s library level. Custom <code class=\"language-text\">shouldComponentUpdate</code> implementations are encouraged when <code class=\"language-text\">PureComponent</code> may not be possible (e.g. an entire data structure object is required as a prop to render a component, like a <a href=\"https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object.html\">Tweet object</a>).</p>\n<blockquote>\n<p>Note: I found this in some archives of half-finished blog posts written sometime between 2015 and 2018. Some items may be a bit dated, the patterns not super new, but I hope that the lessons and ideas are helpful either way.</p>\n</blockquote>\n<h2>New Arrays and Objects</h2>\n<p>Most components use (or should use) shallow comparisons against their props and state during <code class=\"language-text\">shouldComponentUpdate</code> (or in <code class=\"language-text\">PureComponent</code>) in order to determine whether or not they need to be updated and re-rendered.</p>\n<p>When a property is an object or array, every time a new instance of one is passed in, it will fail shallow comparison and trigger an update. Take the following scenarios:</p>\n<h3>Static data</h3>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">  render() {\n    return (\n<span class=\"token deleted\">-     &lt;Component locationState={{ foo: 'bar' }} /></span>\n    );\n  }</code></pre></div>\n<p>In the above case, every time <code class=\"language-text\">render</code> is called, a new object with the same key/value pairs will be sent in <em>every</em> time. This is unnecessary, since the data is static. It can easily be alleviated by hoisting the static data outside of any lifecycle.</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\"><span class=\"token inserted\">+ const locationState = { foo: 'bar' };</span>\n\n// ...\n\n  render() {\n    return (\n<span class=\"token inserted\">+     &lt;Component locationState={locationState} /></span>\n    );\n  }</code></pre></div>\n<p>Now, <code class=\"language-text\">this.props.locationState</code> will always be stricly equal (<code class=\"language-text\">===</code>) to <code class=\"language-text\">nextProps.locationState</code> and not trigger an update/re-render when using a <code class=\"language-text\">PureComponent</code>.</p>\n<h3>Building from instance props or state</h3>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">  render() {\n<span class=\"token deleted\">-   const { item1, item2 } = this.props;</span>\n    return (\n<span class=\"token deleted\">-     &lt;Component data={[ item1, item2 ]} /></span>\n    );\n  }</code></pre></div>\n<p>In this case, a sub-component is dependent on the current component’s props and state. It may not be possible to prevent rendering at the parent level, so we need another solution to avoid creating a new array instance of these properties if they haven’t changed.</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\"><span class=\"token inserted\">+ import createInstanceSelector from 'modules/create-instance-selector';</span>\n\n// ...\n\n  render() {\n    return (\n<span class=\"token inserted\">+     &lt;Component data={this.getData()} /></span>\n    );\n  }\n\n<span class=\"token inserted\">+ getData = createInstanceSelector(</span>\n<span class=\"token inserted\">+   (props, state) => props.item1,</span>\n<span class=\"token inserted\">+   (props, state) => props.item2,</span>\n<span class=\"token inserted\">+   (item1, item2) => [ item1, item2 ]</span>\n<span class=\"token inserted\">+ );</span></code></pre></div>\n<p>We have a module called <code class=\"language-text\">createInstanceSelector</code> for this purpose. It works like <code class=\"language-text\">reselect</code>’s <code class=\"language-text\">selectors</code>, in that it memoizes composable, derived data. In the above case, <code class=\"language-text\">this.getData()</code> will only return a new array if either <code class=\"language-text\">props.item1</code> or <code class=\"language-text\">props.item2</code> has changed since the last call. Here’s an example of how you might implement that:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> createSelector <span class=\"token keyword\">from</span> <span class=\"token string\">'reselect'</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token punctuation\">(</span>componentInstance<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>selectors<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> instanceSelector <span class=\"token operator\">=</span> <span class=\"token function\">createSelector</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>selectors<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>instance <span class=\"token operator\">=</span> componentInstance<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    <span class=\"token function\">instanceSelector</span><span class=\"token punctuation\">(</span>instance<span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">,</span> instance<span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token operator\">...</span>instance <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2>New Functions</h2>\n<p>Similar to arrays and objects, every time a function is defined, it will fail strict equality checks. There are multiple ways to ensure you don’t create a new function on each render cycle. Pick the one that works best for you.</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">  render() {\n    return (\n<span class=\"token deleted\">-     &lt;Component identityFunction={(item) => item.id} /></span>\n    );\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\"><span class=\"token inserted\">+ const identityFunction = (item) => item.id;</span>\n  // ...\n  render() {\n    return (\n<span class=\"token inserted\">+     &lt;Component identityFunction={identityFunction} /></span>\n    );\n  }</code></pre></div>\n<p><em>Tip: You can use the <a href=\"https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md\">eslint-plugin-react/jsx-no-bind</a> rule to disallow the undesired pattern</em></p>\n<h2>Components</h2>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">  render() {\n    return (\n<span class=\"token deleted\">-     &lt;Component button={&lt;Button />} /></span>\n    )\n  }</code></pre></div>\n<h2>Functions that render sub-trees</h2>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">  class ParentComponent extends Component {\n    render() {\n      return (\n<span class=\"token deleted\">-       &lt;Component renderButtons={this._renderButtons}</span>\n      );\n    }\n\n<span class=\"token deleted\">-   _renderButtons = () => {</span>\n<span class=\"token deleted\">-     const { items } = this.props;</span>\n<span class=\"token deleted\">-     return items.map((item, i) => &lt;Item data={item} key={i} />);</span>\n<span class=\"token deleted\">-   };</span>\n  }</code></pre></div>\n<h3>When to use this pattern</h3>\n<ul>\n<li>When the output of <code class=\"language-text\">_renderButtons</code> <em>must not</em> change</li>\n<li>When <code class=\"language-text\">Component</code> <em>must</em> be re-rendered on every cycle</li>\n</ul>\n<h3>When not to use this pattern</h3>\n<ul>\n<li>When <code class=\"language-text\">Component</code> is a <code class=\"language-text\">PureComponent</code></li>\n</ul>\n<p>Keep in mind, performance is almost always application specific. When you use <code class=\"language-text\">PureComponent</code> or <code class=\"language-text\">shouldComponentUpdate</code> are up to your needs. Take everything here as a starting guideline and not an end-all, be-all.</p>","frontmatter":{"title":"Prop type performance considerations for React","date":"January 03, 2019"}}},"pageContext":{"slug":"/02-prop-types/","previous":{"fields":{"slug":"/01-new-blog/"},"frontmatter":{"title":"A new blog"}},"next":null}}