{"componentChunkName":"component---src-templates-blog-post-js","path":"/two-simple-tips-on-using-react-hooks/","result":{"data":{"site":{"siteMetadata":{"title":"Colloque's Blog","siteUrl":"https://blog.colloque.io"}},"markdownRemark":{"id":"90a3591c-6594-5553-a9a5-0c1301a3c926","excerpt":"I have been using React Hooks for a few months now and I am absolutely loving it! Today I would like to share 2 simple tips that I have learned along the way…","html":"<p>I have been using React Hooks for a few months now and I am absolutely loving it! Today I would like to share 2 simple tips that I have learned along the way. Hopefully you will find them useful!</p>\n<h3>useEffect for API requests</h3>\n<p>One common use case for <code class=\"language-text\">useEffect</code> hook is to fetch some data from REST API and display them:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fetchUserData</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token constant\">API</span><span class=\"token punctuation\">.</span><span class=\"token function\">getUserData</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t\t<span class=\"token function\">setUserData</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>userData<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token function\">fetchUserData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This looks fine at first glance but there are actually 2 potential bugs here:</p>\n<ol>\n<li>If user navigate away before the API returns, this will cause a set state to an unmounted component which may lead to memory leak.</li>\n<li>If <code class=\"language-text\">userId</code> changes before the previous request returns, this will cause the component to send another request which may lead to race condition (The first request returns after the second one and override the state).</li>\n</ol>\n<p>To prevent those bugs we can simply add a local variable <code class=\"language-text\">ignore</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">let</span> ignore <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fetchUserData</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token constant\">API</span><span class=\"token punctuation\">.</span><span class=\"token function\">getUserData</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t\t<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>ignore<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t\t\t<span class=\"token function\">setUserData</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>userData<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t\t<span class=\"token punctuation\">}</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token function\">fetchUserData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t\tignore <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><code class=\"language-text\">ignore</code> will be set to true when ever the component is unmounted or the dependency array has changed (<code class=\"language-text\">userId</code> changed in this case). Therefore <code class=\"language-text\">setUserData</code> will not be called!</p>\n<p>This pattern is documented in <a href=\"https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies\">React’s official documentation</a>.</p>\n<h3>useDeepMemo</h3>\n<p>Sometimes when we write custom hooks we want that hook to be able to take a dynamic object or array as input. For example consider this <code class=\"language-text\">useAPI</code> custom hook:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// usage</span>\n<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token function\">useAPI</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n\turl<span class=\"token operator\">:</span> ‘<span class=\"token operator\">/</span>api<span class=\"token operator\">/</span><span class=\"token keyword\">get</span><span class=\"token operator\">-</span>user’<span class=\"token punctuation\">,</span>\n\tbody<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> userId <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// implementation</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">useAPI</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> url<span class=\"token punctuation\">,</span> body <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>response<span class=\"token punctuation\">,</span> setResponse<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token comment\">// do the request here</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>url<span class=\"token punctuation\">,</span> body<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token keyword\">return</span> response<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>This is a nice handy custom hook, but there is just one problem. Whenever there is a re-render, <code class=\"language-text\">body</code> is passed in as a new object and <code class=\"language-text\">useEffect</code> will get triggered again. This will cause an infinite loop!</p>\n<p>Sure, we can wrap the body object in an <code class=\"language-text\">useMemo</code> hook, but it becomes a pain in the ass when you have to do that every time you use <code class=\"language-text\">useAPI</code>!</p>\n<p>To solve this, we can write another custom hook <code class=\"language-text\">useDeepMemo</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// you can also use other deep compare functions</span>\n<span class=\"token comment\">// e.g. lodash’s _.isEqual</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> equal <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> ‘@wry<span class=\"token operator\">/</span>equality’<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useDeepMemo</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">memoFn<span class=\"token punctuation\">,</span> key</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>ref<span class=\"token punctuation\">.</span>current <span class=\"token operator\">||</span> <span class=\"token operator\">!</span><span class=\"token function\">equal</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ref<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> key<span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token function\">memoFn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>I first saw this technique from <a href=\"https://github.com/apollographql/apollo-client/blob/master/src/react/hooks/utils/useDeepMemo.ts\">Apollo’s source code</a>. This is a replacement for <code class=\"language-text\">useMemo</code> but it uses deep equality to compare memo keys and it guarantees that the memo function will only be called if the keys are unequal.</p>\n<p>With that, we can then rewrite our <code class=\"language-text\">useAPI</code> hook:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">useAPI</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> url<span class=\"token punctuation\">,</span> body <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>response<span class=\"token punctuation\">,</span> setResponse<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token keyword\">const</span> cachedBody <span class=\"token operator\">=</span> <span class=\"token function\">useDeepMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> body<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>body<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token comment\">// do the request here</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>url<span class=\"token punctuation\">,</span> cachedBody<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token keyword\">return</span> response<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Now we can use <code class=\"language-text\">useAPI</code> without worrying about accidentally causing an infinite loop!</p>","frontmatter":{"title":"Two simple tips on using React Hooks","date":"February 15, 2020"}}},"pageContext":{"slug":"/two-simple-tips-on-using-react-hooks/","previous":null,"next":{"fields":{"slug":"/using-dataloader-to-batch-api-requests/"},"frontmatter":{"title":"Using DataLoader to batch API requests"}}}}}