tezvyn:

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

AI-drafted, machine-checkedSource: github.comadvanced

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 THIS TESTS: 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.

A GOOD ANSWER COVERS: 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.

COMMON WRONG ANSWERS: 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.

LIKELY FOLLOW-UPS: 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?

ONE 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.

Read the original → github.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.