Skip to content
tezvyn:

Why is exhaustive-deps critical and when can you disable it?

Source: github.comHardHow cards are made

This tests closure and effect timing intuition. A strong answer explains missing deps let effects read stale render values, gives a concrete override like a ref or stable callback, and warns that silencing the rule hides refactor hazards.

What's really being asked

Whether you understand that a useEffect callback is a closure created during a specific render, so any variable it reads must be in the dependency array to ensure the effect re-runs when that value changes. Missing deps do not cause the effect to see the latest value magically; they cause it to see the value from the render where the closure was formed, which is a classic stale closure bug.

The full answer

First, explain the mechanism. When React runs the effect, it executes the function created during the last render. If that function references props or state that were not listed in deps, it uses the old captured values, not the current ones. Second, describe a legitimate disable case. One valid example is when you intentionally want to read the latest value without re-running the effect, such as using a ref inside an interval or event listener where the ref is excluded because refs are mutable and do not trigger re-renders anyway. Another is when a dependency is a function that is truly stable by contract but the linter cannot prove it, like a setState from useState or a callback wrapped in useCallback with an empty deps array. Third, state the risk. Disabling the rule means future teammates can add variables to the effect body without realizing they are stale, turning a compile-time safety net into a latent bug. Fourth, mention the fix pattern. If the linter complains, the right path is usually to add the missing dependency or to lift a value into a ref when you need the latest version without subscribing to it.

The mistakes people make

Saying the rule is just a style guide or performance optimization. Claiming you disable it because it is too noisy without naming a specific semantic reason. Suggesting you can safely omit deps if you do not think they will change. Arguing that useEffect should read variables from a higher scope automatically. These all reveal a shallow model of closures.

What usually comes next

How would you refactor an effect that needs the latest prop value inside an interval without listing that prop as a dependency? What is the difference between a ref and state in this context? How does the React Compiler change the need for manual dependency arrays?

A concrete example

Imagine a chat component with a prop called roomId and an effect that joins a room. If you write useEffect(() => { connect(roomId); }, []) the roomId is missing from deps. When the parent switches rooms, the effect does not re-run, and the closure still sees the original roomId, so the user stays connected to the wrong room. A valid override might be a polling effect that reads a ref to a latest flag: useEffect(() => { const id = setInterval(() => { if (isMountedRef.current) fetch(); }, 5000); return () => clearInterval(id); }, []) where isMountedRef is intentionally excluded because it is a mutable ref and does not need to trigger re-sync, though even then the safer pattern is to use a stable callback or the ref inside a layout effect.

Interview question

How should you refactor an effect that needs the latest prop value inside a polling interval without listing that prop as a dependency?

  • a.Disable the exhaustive-deps rule for that effect and read the prop directly.
  • b.Lift the prop value into a ref and read the ref inside the interval with an empty dependency array.Correct
  • c.Move the polling logic into a layout effect to capture the prop before the browser paints.
  • d.Add the prop to the dependency array and clear the interval in cleanup so it restarts with the new value.
Why?

Lifting the prop into a ref lets the interval read the latest value without re-subscribing, because refs are mutable and do not trigger re-renders. Disabling the rule and reading the prop directly hides the stale closure from the linter and leaves the code vulnerable to refactor hazards.

Just read this? Test yourself on what you have been reading.

Read the original → github.com

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on react — each one lists the topics its interview covers.

See open roles