Runnable lesson · press Try it on the examples to edit and run them here.
Effects are an escape hatch to talk to the world outside React. Many bugs come from using them for jobs React handles better. This lesson shows the five most common cases and the simpler replacement for each.
1. Calculating a value from props or state
Instead of an effect that copies a result into state:
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(first + " " + last);
}, [first, last]);
This renders twice for every change (once with the old fullName, once after the effect), and fullName can briefly be wrong. Just calculate it:
function App() {
const [first, setFirst] = React.useState("Ada");
const [last, setLast] = React.useState("Lovelace");
const fullName = first + " " + last; // calculated during render
return (
<div>
<input aria-label="First" value={first} onChange={(e) => setFirst(e.target.value)} />
<input aria-label="Last" value={last} onChange={(e) => setLast(e.target.value)} />
<p>{fullName}</p>
</div>
);
}
2. Responding to a user event
If something happens because the user clicked, put the code in the click handler. An effect cannot tell why a value changed, so it may run at the wrong time.
// Instead of this:
useEffect(() => {
if (submitted) post("/api/save", data);
}, [submitted]);
// Do this:
function handleSubmit() {
post("/api/save", data);
}
Use effects for things that should happen because the component is on the screen (a chat connection), not because the user did something.
3. Resetting state when a prop changes
// Instead of this:
useEffect(() => { setComment(""); }, [postId]);
Give the component a key, so React resets everything cleanly; see useState in Depth:
<CommentBox key={postId} postId={postId} />
4. Chains of effects
An effect that sets state, which triggers another effect that sets more state, is slow and fragile. Calculate what you can during render, and do the rest in the event handler that started it.
5. Notifying a parent after state changes
// Instead of an effect calling onChange(value) after state updates...
useEffect(() => { onChange(isOn); }, [isOn]);
// ...call it in the same event handler that changes the state:
function handleClick() {
setIsOn(!isOn);
onChange(!isOn);
}
Even better: let the parent own the state (lifting state up), so the child has nothing to notify. See Lifting State Up.
When an effect IS right
| Good use | Why an effect |
|---|---|
| Connect to a chat server while the component is shown | It must start on mount and stop on unmount |
Add and remove a window event listener | Outside system |
| Control a third-party widget such as a map | Outside system |
| Send an analytics event because a page was displayed | The reason is “it appeared”, not a click |
| Fetch data when a component shows | A basic version; libraries do it better |
A quick test
Ask: “Why does this code need to run?”
- Because the user did something → event handler.
- Because this component is on the screen → effect.
- Because some data changed and I need a new value from it → calculate during render.
Key points
- Compute derived values during render, not in effects.
- Handle user actions in event handlers.
- Use effects only to synchronise with something outside React.