Side Effects with useEffect
useEffect runs code after a component renders — fetching
data, subscribing to something, or manually touching the DOM.
Example: a live-updating clock
import { useState, useEffect } from "react";
function Clock() {
const [time, setTime] = useState(new Date().toLocaleTimeString());
useEffect(() => {
const id = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);
return () => clearInterval(id);
}, []);
return <p>Current time: {time}</p>;
}
The second argument, [] — an empty
dependency array — tells React "only run this effect
once, right after the very first render," rather than after every single
re-render. Change it to [time] and the effect would instead
re-run every time time changes — usually not what you'd
want here, since it would reset the interval constantly instead of
letting it tick steadily.
Why the function returned inside useEffect matters
The function returned from the effect (() => clearInterval(id))
is cleanup — React calls it automatically if the
component is removed from the page, stopping the interval so it doesn't
keep silently running (and wasting resources) after the clock is no
longer even visible. Forgetting cleanup for things like intervals,
timers, and subscriptions is a very common source of subtle bugs in real
React apps.
import { useState, useEffect } from "react";
function Clock() {
const [time, setTime] = useState(new Date().toLocaleTimeString());
useEffect(() => {
const id = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);
return () => clearInterval(id);
}, []);
return <p>Current time: {time}</p>;
}
Renders the current time as text, updating once per second.