Effect Dependencies: Stay Synchronized with Current Values
Effect Dependencies: Stay Synchronized with Current Values
An attendance page switches classes, but a title Effect keeps using 7A. The code reads the changing class while claiming that no changing dependency matters.
An Effect dependency is a reactive value used by its synchronization: a prop, state value or value created in the component body. Dependencies tell React when the synchronization may need to run again; they are determined by the code, not chosen to achieve a preferred call count.
Understand
Identify values read by an Effect.
Build
Keep a title current when its class changes.
Check
Refactor unstable dependencies instead of suppressing warnings.
Values used by synchronization define its inputs
Read
The Effect reads className.
Compare
React compares dependencies with Object.is.
Re-synchronize
A changed class triggers cleanup and setup.
The array describes the Effect’s changing inputs
With no dependency array, an Effect runs after each commit. An empty array says it has no reactive dependencies. An array such as [className] makes React compare that value with the previous committed dependency using Object.is. An unchanged primitive can skip another setup; a newly created object has a different identity even if its fields look equal.
The dependency linter helps find values your Effect reads. Removing className from the array does not stop className from changing; it stops the Effect from following that change. The resulting closure can keep the value from an earlier render, which is a synchronization bug.
Make the class an explicit dependency
The title-setting work reads className, so className belongs in the dependency array. The React state setter is stable and does not need an added dependency in this Effect. Clicking Choose 7B changes the rendered class and the tab title.
React · JavaScript with JSX
import { useEffect, useState } from 'react';
export default function App() {
const [className, setClassName] = useState('7A');
useEffect(() => {
const oldTitle = document.title;
document.title = 'Class ' + className;
return () => { document.title = oldTitle; };
}, [className]);
return <main><h1>Current class</h1><p>{className}</p>
<button onClick={() => setClassName('7B')}>Choose 7B</button></main>;
}Expected screen and behavior
The page and tab title follow 7A, then 7B after the button click.Follow the dependency across two commits
React · Make the class an explicit dependency
import { useEffect, useState } from 'react';
export default function App() {
const [className, setClassName] = useState('7A');
useEffect(() => {
const oldTitle = document.title;
document.title = 'Class ' + className;
return () => { document.title = oldTitle; };
}, [className]);
return <main><h1>Current class</h1><p>{className}</p>
<button onClick={() => setClassName('7B')}>Choose 7B</button></main>;
}The first committed Effect sees 7A. After the event schedules 7B, the next render creates an Effect closure using 7B and an array containing 7B. React compares 7A and 7B, then runs the previous cleanup and the new setup.
If the array were empty, the new class would still appear in JSX, but the synchronization would not be requested for that changed class. A dependency is not a parameter passed into your callback: the callback reads its surrounding bindings from that render.
Remove unnecessary object dependencies through structure
Suppose an Effect needs an options object built from className. Creating that object in the component gives it a new identity on every render. If it is only used by the Effect, create it inside the Effect and depend on className itself. This expresses the actual input without adding an object solely to stabilize it.
For state updates that depend on previous state, a functional updater can avoid reading that state in a callback when appropriate. Do not blindly remove a dependency: first change the code so the Effect genuinely no longer reads the reactive value. Use memoization only when it is justified, not as a substitute for understanding.
Try the school task
React · starting example
import { useEffect, useState } from 'react';
export default function App() {
const [className, setClassName] = useState('7A');
useEffect(() => {
const oldTitle = document.title;
document.title = 'Class ' + className;
return () => { document.title = oldTitle; };
}, [className]);
return <main><h1>Current class</h1><p>{className}</p>
<button onClick={() => setClassName('7B')}>Choose 7B</button></main>;
}Change the title to include the selected student and a report kind. Both values can change, so include both dependencies. Use separate buttons to observe each change.
Worked answer
The Effect reads student and kind and lists both. Either changed string causes the title to follow the current report. The displayed summary is still calculated directly from the same values.
React · JavaScript with JSX
import { useEffect, useState } from 'react';
export default function App() {
const [student, setStudent] = useState('Maya');
const [kind, setKind] = useState('Marks');
useEffect(() => {
const oldTitle = document.title;
document.title = kind + ': ' + student;
return () => { document.title = oldTitle; };
}, [student, kind]);
return <main><h1>Report inputs</h1><p>{kind}: {student}</p>
<button onClick={() => setStudent('Ravi')}>Choose Ravi</button>
<button onClick={() => setKind('Attendance')}>Attendance report</button></main>;
}Expected screen and behavior
Choosing Ravi changes Marks: Maya to Marks: Ravi. Attendance report then changes the summary to Attendance: Ravi.Mistakes and boundaries
Suppressing a dependency warning can hide stale values. Fix the underlying relationship, or explain a carefully supported nonreactive design rather than adding a disabling comment by default.
An Effect that updates a dependency on every setup can produce an update loop. Ask whether the state is derived or whether a separate event should perform the update.
Dependency arrays have a fixed number of entries written inline. They do not select which branch of a Hook runs; the Hook itself must follow the Rules of Hooks.
Check your reasoning
React · Make the class an explicit dependency
import { useEffect, useState } from 'react';
export default function App() {
const [className, setClassName] = useState('7A');
useEffect(() => {
const oldTitle = document.title;
document.title = 'Class ' + className;
return () => { document.title = oldTitle; };
}, [className]);
return <main><h1>Current class</h1><p>{className}</p>
<button onClick={() => setClassName('7B')}>Choose 7B</button></main>;
}Choose the answer that follows the data and the interaction, then read the explanation for each alternative.
Carry the idea forward
Dependencies describe the values used by synchronization. Refactor the work when the inputs are wrong rather than silencing the linter. Review render snapshots and cleanup before the next setup.
Read the primary reference: React: removing Effect dependencies.
Reaching the recap and completing the quizzes are separate milestones.