[{"data":1,"prerenderedAt":182},["ShallowReactive",2],{"qa-\u002Freact\u002Fhooks\u002Fuseeffect":3},{"page":4,"siblings":173,"blog":179},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"related":164,"seo":165,"seoDescription":166,"stem":167,"subtopic":168,"topic":169,"topicSlug":170,"updated":171,"__hash__":172},"qa\u002Freact\u002Fhooks\u002Fuseeffect.md","Useeffect",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{},true,"\u002Freact\u002Fhooks\u002Fuseeffect",[22,27,31,35,39,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160],{"id":23,"difficulty":24,"q":25,"a":26},"what-is-useeffect","easy","What does useEffect do?","`useEffect` lets you run **side effects** — work that reaches outside React's\nrender output: fetching data, setting up subscriptions or timers, manually\ntouching the DOM, logging. Render must stay pure (no side effects), so React\ngives you `useEffect` as the escape hatch that runs **after** the component has\nrendered and the screen is updated.\n\n```jsx\nuseEffect(() => {\n  document.title = `${count} unread`\n}, [count])\n```\n\nThe mental model isn't \"run this on mount\u002Fupdate\" but \"**keep this external\nthing in sync** with the state and props in my dependency array.\" React runs\nthe effect whenever one of those inputs changes so the outside world matches\nthe latest render.\n",{"id":28,"difficulty":14,"q":29,"a":30},"deps-array","What does the dependency array control?","The dependency array tells React **when to re-run** the effect by comparing\neach item to its value from the previous render (using `Object.is`):\n\n- `[]` -> run **once** after the first render (no dependencies ever change).\n- `[a, b]` -> run after the first render, then again **only when `a` or `b`\n  change**.\n- **omitted** -> run after **every** render.\n\n```jsx\nuseEffect(() => {\n  const sub = source.subscribe(id)\n  return () => sub.unsubscribe()\n}, [id]) \u002F\u002F re-subscribe only when `id` changes\n```\n\nThe golden rule (enforced by the `react-hooks\u002Fexhaustive-deps` lint): every\nreactive value the effect *reads* — props, state, derived values — must be\nlisted. Omitting one to \"run less often\" is the #1 source of stale-data bugs.\n",{"id":32,"difficulty":14,"q":33,"a":34},"cleanup","What is the cleanup function and when does it run?","If your effect sets something up that needs tearing down, **return a function**\nfrom it — that's the cleanup. React runs it in two situations: **before\nre-running** the effect (to undo the previous run) and when the component\n**unmounts**. This prevents leaked listeners, duplicate subscriptions, and\n\"setState on unmounted component\" warnings.\n\n```jsx\nuseEffect(() => {\n  const id = setInterval(tick, 1000)\n  return () => clearInterval(id) \u002F\u002F tear down before next run \u002F on unmount\n}, [])\n```\n\nThe sequence on a dependency change is: run cleanup for the *old* deps -> run\nthe effect for the *new* deps. So with `[id]`, changing `id` from `1` to `2`\nunsubscribes from `1` first, then subscribes to `2`. Forgetting cleanup is how\nyou end up with N intervals firing after N renders.\n",{"id":36,"difficulty":24,"q":37,"a":38},"empty-deps","What happens with an empty dependency array?","An empty array `[]` means \"no reactive dependencies,\" so the effect runs\n**once** after the initial render and its cleanup runs **once** on unmount.\nIt's the closest hooks equivalent to the old `componentDidMount` +\n`componentWillUnmount` lifecycle pair.\n\n```jsx\nuseEffect(() => {\n  const onResize = () => setWidth(window.innerWidth)\n  window.addEventListener('resize', onResize)\n  return () => window.removeEventListener('resize', onResize)\n}, []) \u002F\u002F attach once, detach on unmount\n```\n\nCaveat for interviews: in React 18 **Strict Mode during development**, React\nintentionally mounts -> unmounts -> remounts components, so your `[]` effect and\nits cleanup fire twice. That's a deliberate check that your cleanup is correct;\nit does not happen in production.\n",{"id":40,"difficulty":41,"q":42,"a":43},"stale-closure","hard","What is a stale closure in useEffect?","An effect closes over the props and state from the render it was created in.\nIf you read a value but **leave it out of the dependency array**, the effect\nkeeps using the *frozen* value from that render and never sees updates — a\nstale closure.\n\n```jsx\n\u002F\u002F count is captured once (as 0) and never updated -> logs 0, 0, 0...\nuseEffect(() => {\n  const id = setInterval(() => console.log(count), 1000)\n  return () => clearInterval(id)\n}, []) \u002F\u002F missing `count`\n\n\u002F\u002F Option A: list the dependency (re-creates the interval each change)\n\u002F\u002F Option B: use a functional updater so you don't read `count` at all\nsetCount(c => c + 1)\n```\n\nFixes: include every value you read in the deps, use the functional updater\nform so the stale value never matters, or stash the latest value in a `useRef`\nwhen you deliberately want a long-lived effect that reads fresh data.\n",{"id":45,"difficulty":41,"q":46,"a":47},"effect-timing","How does useEffect differ from useLayoutEffect?","Both run after render, but at different moments relative to the browser\n**paint**:\n\n- `useEffect` runs **asynchronously, after the browser has painted**. The user\n  sees the new frame, then the effect fires. Best for the vast majority of\n  effects (data, subscriptions) since it doesn't block visual updates.\n- `useLayoutEffect` runs **synchronously after DOM mutations but before\n  paint**. React blocks painting until it finishes, so you can measure layout\n  or mutate the DOM and the user never sees an intermediate state.\n\n```jsx\nuseLayoutEffect(() => {\n  const { height } = ref.current.getBoundingClientRect()\n  setTooltipTop(height) \u002F\u002F adjust position before the browser paints\n}, [])\n```\n\nUse `useLayoutEffect` only when you must read\u002Fwrite layout to avoid a visible\nflicker; because it's blocking, overusing it hurts performance. (On the server\nit doesn't run and warns — guard SSR code accordingly.)\n",{"id":49,"difficulty":14,"q":50,"a":51},"data-fetching","How do you fetch data inside useEffect?","Start the request in the effect and store the result in state. List every value\nthe request depends on (like an `id`) in the dependency array so it refetches\nwhen they change.\n\n```jsx\nuseEffect(() => {\n  let active = true\n  fetch(`\u002Fapi\u002Fuser\u002F${id}`)\n    .then(r => r.json())\n    .then(data => { if (active) setUser(data) })\n  return () => { active = false } \u002F\u002F ignore a stale response\n}, [id])\n```\n\nThe `active` flag (or an `AbortController`) prevents a slow earlier request from\noverwriting a newer one. In real apps a data library usually handles this better.\n",{"id":53,"difficulty":41,"q":54,"a":55},"fetch-race-condition","How do you avoid race conditions when fetching in an effect?","If `id` changes quickly, responses can arrive **out of order** and the older one\noverwrites the newer. Guard with a cleanup that invalidates the in-flight\nrequest — an ignore flag or an `AbortController`.\n\n```jsx\nuseEffect(() => {\n  const ctrl = new AbortController()\n  fetch(`\u002Fapi\u002F${id}`, { signal: ctrl.signal })\n    .then(r => r.json())\n    .then(setData)\n    .catch(e => { if (e.name !== 'AbortError') throw e })\n  return () => ctrl.abort() \u002F\u002F cancel the previous request\n}, [id])\n```\n\nReact runs the cleanup before the next effect, so the stale request is aborted\nand can't clobber fresh data.\n",{"id":57,"difficulty":14,"q":58,"a":59},"fetch-vs-library","Why prefer a data library over fetching in useEffect?","Hand-rolled `useEffect` fetching means you reimplement caching, deduping,\nretries, race-condition handling, loading\u002Ferror states, and refetching — for\nevery call. Libraries like **React Query \u002F SWR \u002F RTK Query** give you all of that\ndeclaratively.\n\n```jsx\nconst { data, isLoading, error } = useQuery({\n  queryKey: ['user', id],\n  queryFn: () => fetchUser(id),\n})\n```\n\nThe React docs explicitly recommend a framework or data library for fetching.\nUse raw effect-fetching only for simple, one-off cases.\n",{"id":61,"difficulty":41,"q":62,"a":63},"object-deps","Why does an object or array dependency re-run the effect every render?","Dependencies are compared by **reference** (`Object.is`). An object\u002Farray literal\ncreated during render is a **new reference each time**, so the effect sees a\n\"changed\" dependency on every render and re-runs endlessly.\n\n```jsx\n\u002F\u002F options is a new object every render -> effect runs every render\nconst options = { id }\nuseEffect(() => subscribe(options), [options])\n```\n\nFixes: depend on the **primitive** inside (`[id]`), build the object **inside**\nthe effect, or memoize it with `useMemo`. Same applies to functions passed as\ndeps — stabilize with `useCallback`.\n",{"id":65,"difficulty":41,"q":66,"a":67},"usecallback-dep","How does useCallback help with effect dependencies?","A function defined in a component body is recreated each render (new reference).\nIf an effect depends on it, the effect re-runs every render. `useCallback`\nreturns a **stable** function identity that only changes when its own deps do.\n\n```jsx\nconst load = useCallback(() => fetchData(id), [id])\nuseEffect(() => { load() }, [load]) \u002F\u002F re-runs only when id changes\n```\n\nWithout it, the effect would fire on every render. `useCallback` is mainly about\n**referential stability** for deps and memoized children — not raw speed.\n",{"id":69,"difficulty":14,"q":70,"a":71},"usememo-dep","How do you stabilize an object dependency with useMemo?","Wrap the object's creation in `useMemo` so it keeps the same reference until its\ninputs change, which keeps a dependent effect from re-running needlessly.\n\n```jsx\nconst filters = useMemo(() => ({ status, sort }), [status, sort])\nuseEffect(() => {\n  applyFilters(filters)\n}, [filters]) \u002F\u002F only when status or sort actually change\n```\n\nEquivalent alternative: skip the object and depend on `[status, sort]` directly.\nMemoize when the object must be passed around as a single value.\n",{"id":73,"difficulty":14,"q":74,"a":75},"polling-interval","How do you poll an API on an interval with useEffect?","Set up the interval in the effect and **clear it in cleanup** so it doesn't leak\nor duplicate when deps change or the component unmounts.\n\n```jsx\nuseEffect(() => {\n  const id = setInterval(() => refetch(), 5000)\n  return () => clearInterval(id)\n}, [refetch])\n```\n\nIf `refetch` isn't stable, wrap it in `useCallback` or you'll tear down and\nrecreate the interval each render. For variable intervals, a `useRef`-based\n`useInterval` hook is a common pattern.\n",{"id":77,"difficulty":41,"q":78,"a":79},"debounce-effect","How do you debounce a value with useEffect?","Start a timer in the effect that updates a \"debounced\" state, and **clear the\ntimer in cleanup** — so rapid changes keep resetting the timer until input\nsettles.\n\n```jsx\nconst [query, setQuery] = useState('')\nconst [debounced, setDebounced] = useState(query)\nuseEffect(() => {\n  const id = setTimeout(() => setDebounced(query), 300)\n  return () => clearTimeout(id) \u002F\u002F cancel if query changes again\n}, [query])\n\u002F\u002F run search on `debounced`, not `query`\n```\n\nEach keystroke re-runs the effect, whose cleanup cancels the previous pending\ntimer — so the update only fires after 300ms of quiet.\n",{"id":81,"difficulty":41,"q":82,"a":83},"subscribe-external","How do you subscribe to an external store from an effect?","Subscribe in the effect and **unsubscribe in cleanup**. For external stores,\nReact 18's `useSyncExternalStore` is the purpose-built hook (tear-free with\nconcurrent rendering), but a manual effect works for simple cases.\n\n```jsx\nuseEffect(() => {\n  const unsub = store.subscribe(() => setValue(store.get()))\n  setValue(store.get())   \u002F\u002F sync the initial value\n  return unsub            \u002F\u002F cleanup unsubscribes\n}, [store])\n```\n\nAlways read the current value once on subscribe so you don't miss a change that\nhappened between render and effect. Prefer `useSyncExternalStore` for shared\nstores.\n",{"id":85,"difficulty":14,"q":86,"a":87},"localstorage-sync","How do you sync state to localStorage with useEffect?","Write to `localStorage` in an effect that depends on the value, and read it lazily\nin the initializer so it's only parsed once.\n\n```jsx\nconst [theme, setTheme] = useState(() => localStorage.getItem('theme') ?? 'light')\nuseEffect(() => {\n  localStorage.setItem('theme', theme)\n}, [theme])\n```\n\nThe lazy initializer avoids reading storage on every render; the effect persists\nchanges. Guard `localStorage` access for SSR (it doesn't exist on the server) —\neffects don't run server-side, so this pattern is SSR-safe.\n",{"id":89,"difficulty":41,"q":90,"a":91},"not-need-effect","When do you NOT need a useEffect?","Effects are for **synchronizing with external systems**, not for reacting to user\nevents or computing values. If something happens **because the user did\nsomething**, do it in the **event handler**, not an effect.\n\n```jsx\n\u002F\u002F effect reacting to a click that already happened\nuseEffect(() => { if (submitted) postData() }, [submitted])\n\u002F\u002F just do it in the handler\nfunction onSubmit() { postData() }\n```\n\n\"You Might Not Need an Effect\" (React docs): skip effects for derived data,\nevent responses, and resetting state on prop change (use `key` instead).\n",{"id":93,"difficulty":14,"q":94,"a":95},"derive-not-effect","Why shouldn't you use an effect to compute derived state?","Storing computed data in state and syncing it with an effect causes an **extra\nrender** and risks the copy going stale. Just compute it during render.\n\n```jsx\n\u002F\u002F effect + extra state + extra render\nconst [full, setFull] = useState('')\nuseEffect(() => setFull(`${first} ${last}`), [first, last])\n\n\u002F\u002F derive in render (memoize only if expensive)\nconst full = `${first} ${last}`\n```\n\nAn effect-to-set-state for something derivable is a classic anti-pattern flagged\nin the React docs.\n",{"id":97,"difficulty":41,"q":98,"a":99},"lint-suppress-danger","Why is disabling the exhaustive-deps lint dangerous?","Adding `\u002F\u002F eslint-disable-next-line react-hooks\u002Fexhaustive-deps` to silence a\nmissing dependency doesn't fix the bug — it hides it. The effect keeps using\n**stale** captured values and silently breaks when those values change.\n\n```jsx\n\u002F\u002F suppressing the warning to \"run once\" -> stale `userId`\nuseEffect(() => { fetchData(userId) }, []) \u002F\u002F eslint-disable-line\n```\n\nInstead, fix the root cause: include the dep, move logic inside the effect, use a\nfunctional setter, or stabilize the value with `useCallback`\u002F`useRef`. Suppress\nonly when you fully understand why it's safe.\n",{"id":101,"difficulty":14,"q":102,"a":103},"cleanup-order2","In what order do cleanup and the next effect run?","On a dependency change, React runs the **previous** effect's cleanup **first**,\nthen runs the **new** effect. It never overlaps them.\n\n```jsx\nuseEffect(() => {\n  console.log('subscribe', id)\n  return () => console.log('unsubscribe', id)\n}, [id])\n\u002F\u002F id: 1 -> 2 logs: \"unsubscribe 1\" then \"subscribe 2\"\n```\n\nThis ordering guarantees you tear down the old subscription before setting up the\nnew one — no leaks, no duplicates. On unmount, only the last cleanup runs.\n",{"id":105,"difficulty":41,"q":106,"a":107},"strict-double","Why does my effect run twice in development?","In React 18 **Strict Mode** (development only), React mounts each component,\n**unmounts it, and remounts it** to surface effects that aren't cleanup-safe. So\na mount effect + its cleanup fire twice.\n\n```jsx\nuseEffect(() => {\n  console.log('run')        \u002F\u002F logs twice in dev Strict Mode\n  return () => console.log('cleanup')\n}, [])\n```\n\nIt does **not** happen in production. If double-invocation breaks something\n(e.g. duplicate requests), that's a sign your effect needs proper cleanup or\nshouldn't be an effect at all — Strict Mode is doing its job.\n",{"id":109,"difficulty":14,"q":110,"a":111},"infinite-effect-loop","What causes an infinite loop in useEffect?","Setting state in an effect whose dependency **changes as a result of that state**\n— or using an unstable object\u002Farray\u002Ffunction dependency created each render.\n\n```jsx\n\u002F\u002F sets data -> re-render -> new [] dep -> runs again -> loop\nuseEffect(() => setData(compute()), [{}])\n\n\u002F\u002F depends on the state it updates\nuseEffect(() => setCount(count + 1), [count])\n```\n\nFixes: stabilize deps (memoize objects\u002Ffunctions), depend on primitives, or use\na functional updater so the effect needn't depend on the state it sets.\n",{"id":113,"difficulty":14,"q":114,"a":115},"async-effect-directly","Why can't the useEffect callback be async?","An `async` function returns a **Promise**, but React expects the effect callback\nto return **either nothing or a cleanup function**. Returning a Promise breaks\ncleanup. Define an async function **inside** and call it.\n\n```jsx\n\u002F\u002F async effect returns a Promise, not a cleanup fn\nuseEffect(async () => { await load() }, [])\n\n\u002F\u002F inner async function\nuseEffect(() => {\n  (async () => { await load() })()\n}, [])\n```\n\nThis keeps the return value available for cleanup while still letting you use\n`await` inside.\n",{"id":117,"difficulty":24,"q":118,"a":119},"event-listener-effect","How do you add and remove a global event listener?","Add the listener in the effect and remove **the same function reference** in\ncleanup, so it's detached on unmount and not duplicated on re-runs.\n\n```jsx\nuseEffect(() => {\n  const onKey = e => { if (e.key === 'Escape') close() }\n  window.addEventListener('keydown', onKey)\n  return () => window.removeEventListener('keydown', onKey)\n}, [close])\n```\n\nThe cleanup must reference the **exact** function passed to `addEventListener`\n(an inline arrow in both calls won't match), which is why it's defined once\ninside the effect.\n",{"id":121,"difficulty":14,"q":122,"a":123},"multiple-effects","Should you split logic into multiple effects?","Yes — use **separate effects for unrelated concerns**, each with its own\ndependency array. One effect per responsibility is clearer and avoids re-running\nunrelated logic when only one dependency changes.\n\n```jsx\nuseEffect(() => { document.title = title }, [title])     \u002F\u002F title sync\nuseEffect(() => {\n  const id = connect(roomId)\n  return () => disconnect(id)\n}, [roomId])                                             \u002F\u002F connection\n```\n\nDon't cram title updates and a subscription into one effect just because they're\nin the same component — split by what they synchronize.\n",{"id":125,"difficulty":14,"q":126,"a":127},"effect-run-timing","When exactly does useEffect run relative to rendering?","The sequence is: React renders (calls your component) -> commits changes to the\nDOM -> **browser paints** -> *then* `useEffect` runs asynchronously. So effects\nnever block the visual update.\n\n```\nrender -> commit (DOM updated) -> paint -> useEffect\n```\n\nThis is why you shouldn't read final layout measurements that must be applied\n*before* paint in `useEffect` — use `useLayoutEffect` for those. For most work\n(fetching, subscriptions, logging), running after paint is exactly what you want.\n",{"id":129,"difficulty":41,"q":130,"a":131},"ref-latest-in-effect","How do you read the latest prop\u002Fstate in a long-lived effect without re-subscribing?","Mirror the value in a `useRef` updated each render, and read `ref.current` inside\nthe effect. The effect can keep an empty dep array (set up once) yet always see\nfresh data.\n\n```jsx\nconst cbRef = useRef(onTick)\nuseEffect(() => { cbRef.current = onTick }) \u002F\u002F keep it current\nuseEffect(() => {\n  const id = setInterval(() => cbRef.current(), 1000)\n  return () => clearInterval(id)\n}, []) \u002F\u002F interval created once, always calls the latest onTick\n```\n\nThis is the core of the `useInterval`\u002F`useEventCallback` patterns — avoid stale\nclosures without tearing down the subscription on every change.\n",{"id":133,"difficulty":24,"q":134,"a":135},"effect-once-mount","How do you run an effect only once when the component mounts?","Pass an **empty dependency array**. The effect runs after the first render and\nnever again (its cleanup runs on unmount).\n\n```jsx\nuseEffect(() => {\n  analytics.pageView()\n}, []) \u002F\u002F mount only\n```\n\nCaveat: in dev Strict Mode it runs twice, and the lint rule will warn if the\neffect actually *uses* props\u002Fstate you left out — so \"run once\" should genuinely\nhave no reactive dependencies.\n",{"id":137,"difficulty":14,"q":138,"a":139},"dep-primitive-vs-object","Why are primitive dependencies safer than object dependencies?","Primitives (`string`, `number`, `boolean`) compare by **value**, so equal values\nare \"unchanged\" and the effect doesn't re-run. Objects\u002Farrays\u002Ffunctions compare\nby **reference**, so a freshly created one looks changed every render.\n\n```jsx\nuseEffect(() => {}, [userId])        \u002F\u002F re-runs only when the number changes\nuseEffect(() => {}, [{ userId }])    \u002F\u002F new object each render -> always re-runs\n```\n\nPrefer depending on the **specific primitive fields** you read rather than a\nwhole object — it's both safer and more precise.\n",{"id":141,"difficulty":14,"q":142,"a":143},"server-no-run","Do effects run during server-side rendering?","No. `useEffect` (and `useLayoutEffect`) **do not run on the server** — they only\nrun in the browser after hydration. So effects are the right place for\nbrowser-only APIs (`window`, `localStorage`, `IntersectionObserver`).\n\n```jsx\nuseEffect(() => {\n  const mq = window.matchMedia('(min-width: 768px)') \u002F\u002F browser-only, safe here\n  \u002F\u002F ...\n}, [])\n```\n\nDon't access `window`\u002F`document` during render (it crashes SSR); defer it to an\neffect. `useLayoutEffect` additionally **warns** during SSR — guard or use\n`useEffect` there.\n",{"id":145,"difficulty":41,"q":146,"a":147},"reset-on-prop-change","How do you reset state when a prop changes, without an effect?","Avoid the effect-that-resets-state pattern. Prefer **changing the `key`** to\nremount the subtree, or the conditional set-during-render pattern for partial\nresets.\n\n```jsx\n\u002F\u002F remount on userId change -> all state resets\n\u003CProfile key={userId} userId={userId} \u002F>\n\n\u002F\u002F partial reset during render (no effect, no extra paint)\nconst [prevId, setPrevId] = useState(id)\nif (id !== prevId) { setPrevId(id); setComment('') }\n```\n\nUsing an effect to watch the prop and call setters causes an extra render and is\nflagged as an anti-pattern in the docs.\n",{"id":149,"difficulty":14,"q":150,"a":151},"cleanup-async-fetch","How does the ignore-flag pattern prevent setting state after unmount?","A boolean captured in the effect, flipped in cleanup, lets the async callback\ncheck whether the component is still mounted (and the request still current)\nbefore calling a setter.\n\n```jsx\nuseEffect(() => {\n  let ignore = false\n  load(id).then(data => { if (!ignore) setData(data) })\n  return () => { ignore = true } \u002F\u002F later resolution is ignored\n}, [id])\n```\n\nThis both prevents the \"can't update an unmounted component\" warning and avoids a\nstale earlier request overwriting newer data.\n",{"id":153,"difficulty":41,"q":154,"a":155},"chained-effects-antipattern","Why are chains of effects that trigger each other an anti-pattern?","An effect that sets state, which triggers another effect that sets more state,\ncreates cascading renders that are hard to follow and inefficient — each step is\na separate render pass.\n\n```jsx\n\u002F\u002F effect -> setState -> effect -> setState ...\nuseEffect(() => setB(a + 1), [a])\nuseEffect(() => setC(b * 2), [b])\n```\n\nPrefer computing the values **together during render** (derive `b` and `c` from\n`a`), or do the multi-step update in a single **event handler**. Reserve effects\nfor genuine external synchronization, not internal state cascades.\n",{"id":157,"difficulty":14,"q":158,"a":159},"effect-vs-render-purity","Why must side effects live in useEffect and not in the render body?","Rendering must be **pure** — given the same props\u002Fstate it returns the same JSX\nwith no side effects. React may call your component multiple times, bail out, or\ndiscard renders (concurrent features), so a side effect in render could run\nunexpectedly, repeatedly, or never.\n\n```jsx\n\u002F\u002F side effect during render — runs on every render, breaks purity\ndocument.title = title\n\u002F\u002F after commit, controlled by deps\nuseEffect(() => { document.title = title }, [title])\n```\n\nKeeping effects out of render is what lets React safely re-render and optimize.\n",{"id":161,"difficulty":24,"q":162,"a":163},"analytics-on-change","How do you run code after a specific value changes?","Use an effect that **depends on that value** — it runs after each commit where\nthe value changed.\n\n```jsx\nuseEffect(() => {\n  analytics.track('step_changed', { step })\n}, [step])\n```\n\nThis is the hooks replacement for the class `setState` callback or\n`componentDidUpdate` comparisons: react to the new value in an effect keyed on\nit. Make sure the dependency is the exact value you're reacting to.\n",null,{"description":11},"React useEffect interview questions and answers — the dependency array, cleanup functions, effect timing and common mistakes.","react\u002Fhooks\u002Fuseeffect","useEffect","Hooks","hooks","2026-06-17","WadYDpcEpirDvJ9N1brliOprDPb6eXUhfXGkfT5T7go",[174,178],{"subtopic":175,"path":176,"order":177},"useState","\u002Freact\u002Fhooks\u002Fusestate",1,{"subtopic":168,"path":20,"order":12},{"path":180,"title":181},"\u002Fblog\u002Freact-useeffect-hook-complete-guide","React useEffect Hook — A Complete Guide to Effects, Dependencies & Cleanup",1781808676782]