Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Effects run after the browser paints — React lets the DOM update be visible to the user before running side effects. This ordering is intentional: subscriptions, fetches, and logging shouldn't block the frame. The cleanup function runs before the next effect fires and on unmount, which is how you avoid double-subscription bugs. The dependency array doesn't watch values reactively — it's a hint to React about when to re-run, compared shallowly with Object.is. An empty array means 'run once on mount', but in StrictMode React deliberately mounts twice in development to catch cleanup bugs.
useEffect runs after paint. The returned cleanup runs before the next invocation and on unmount. Each run sees a fresh closure with current props and state.
clearInterval from the cleanup and rapidly toggle the id. Open the browser performance tab. You should see multiple intervals firing simultaneously — that's the leak. Re-add the cleanup and confirm only one interval ticks at a time.[id] to []. Confirm in the console that 'effect fired' only logs once regardless of how many times id changes. Then change the array to [id, ticks] — now the effect (and the interval) re-creates on every tick. Measure the CPU cost with the profiler.import { useState, useEffect } from 'react';
interface TimerProps { id: number }
export function Timer({ id }: TimerProps) {
const [ticks, setTicks] = useState(0);
useEffect(() => {
console.log(`effect fired for id=${id}`);
const interval = setInterval(() => {
setTicks(t => t + 1);
}, 1000);
// cleanup: runs before next effect AND on unmount
return () => {
console.log(`cleanup for id=${id}`);
clearInterval(interval);
};
}, [id]); // re-runs when id changes
return <div>Timer {id}: {ticks} ticks</div>;
}
// Effect with no cleanup — safe because there is nothing to clean up
export function DocumentTitle({ title }: { title: string }) {
useEffect(() => {
document.title = title;
// no cleanup needed: overwriting the title on the next run is fine
}, [title]);
return null;
}