{"id":1419,"date":"2023-10-16T13:29:13","date_gmt":"2023-10-16T16:29:13","guid":{"rendered":"https:\/\/benjaminray.com\/codebase\/?p=1419"},"modified":"2024-03-18T13:28:22","modified_gmt":"2024-03-18T16:28:22","slug":"ignore-first-render-in-reacts-useeffect","status":"publish","type":"post","link":"https:\/\/benjaminray.com\/codebase\/ignore-first-render-in-reacts-useeffect\/","title":{"rendered":"Ignore First Render in React&#8217;s useEffect()"},"content":{"rendered":"<p>Sometimes you want to use useEffect() to catch a change in a state, but only after the initial value has been set. For example, you have a state called selectedId, and you want to do something when the value changes.<\/p>\n<p>You may have defined the state as follows, with an initial value:<\/p>\n<pre><code>const [selectedId, setSelectedId] = useState&lt;number&gt;(0);\n<\/code><\/pre>\n<p>To act on changes to selectedId, you configured a useEffect() statement as follows:<\/p>\n<pre><code>useEffect(() =&gt; {\n  console.log(`selectedId has changed to ${selectedId}. Or the initial value is being set.)`);\n}, [selectedId]);\n<\/code><\/pre>\n<p>This works fine for most cases, but the effect fires on every change as well as when the state is being set to its initial value.<br \/>\nWhat if we only want to detect changes <em>after<\/em> the initial value is set?<\/p>\n<p>Instead of useEffect(), we can use a custom hook:<\/p>\n<pre><code>function useDidUpdateEffect(fn, inputs) {\n  const isMountingRef = useRef(false);\n\n  useEffect(() =&gt; {\n    isMountingRef.current = true;\n  }, []);\n\n  useEffect(() =&gt; {\n    if (!isMountingRef.current) {\n      return fn();\n    } else {\n      isMountingRef.current = false;\n    }\n  }, inputs);\n}\n\n\/\/ Replace useEffect() with useDidUpdateEffect():\nuseDidUpdateEffect(() =&gt; {\n  console.log(`selectedId has changed to ${selectedId}. This won't fire when the initial value is being set.`);\n}, [selectedId]);\n<\/code><\/pre>\n<p>To make it more reusable, I like to add the custom hook in a separate file and import it when needed:<\/p>\n<p>useDidUpdateEffect.tsx:<\/p>\n<pre><code>import React, { useEffect, useRef } from \"react\";\n\nexport const useDidUpdateEffect = (fn: any, inputs: React.DependencyList | undefined) =&gt; {\n  const isMountingRef = useRef(false);\n  useEffect(() =&gt; {\n    isMountingRef.current = true;\n  }, []);\n  useEffect(() =&gt; {\n    if (!isMountingRef.current) {\n      return fn();\n    } else {\n      isMountingRef.current = false;\n    }\n  \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, inputs);\n}\n<\/code><\/pre>\n<p>Add required import statements to myComponent.tsx:<\/p>\n<pre><code>import { useDidUpdateEffect } from \"hooks\/useDidUpdateEffect\";\n\nuseDidUpdateEffect(() =&gt; {\n  console.log(`selectedId has changed to ${selectedId}. This won't fire when the initial value is being set.`);\n}, [selectedId]);\n<\/code><\/pre>\n<p>Source: <a href=\"https:\/\/stackoverflow.com\/a\/53180013\/4669143\" target=\"_blank\" rel=\"noopener\">https:\/\/stackoverflow.com\/a\/53180013\/4669143<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes you want to use useEffect() to catch a change in a state, but only after the initial value has been set. For example, you have a state called selectedId,  [&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[28],"tags":[],"class_list":["post-1419","post","type-post","status-publish","format-standard","hentry","category-react"],"acf":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9GNjN-mT","jetpack_likes_enabled":false,"_links":{"self":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/1419","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/comments?post=1419"}],"version-history":[{"count":8,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/1419\/revisions"}],"predecessor-version":[{"id":1427,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/1419\/revisions\/1427"}],"wp:attachment":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/media?parent=1419"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/categories?post=1419"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/tags?post=1419"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}