When Two Siblings Need the Same Value, Their Parent Owns It
Lifting State Up
Move state to the lowest common parent of the components that need it. Pass the value down via props and a setter back up via a callback.
What you'll learn
- Recognize when state belongs in a parent
- Pass state down, send updates up
When two sibling components both need to read or write the same value, neither one can own that state alone. The fix: move the state up to their common parent.
The Problem
Imagine two components that should stay in sync:
function Page() {
return (
<>
<Slider /> {/* sets a value */}
<Readout /> {/* shows the same value */}
</>
);
} If Slider owns the state, Readout can’t see it. If Readout
owns it, Slider can’t change it. Either way, it’s stuck.
The Fix — Lift It Up
Move the state to the parent. Pass the value down to one child via a prop, and pass a setter down to the other:
function Page() {
const [value, setValue] = useState(0);
return (
<>
<Slider value={value} onChange={setValue} />
<Readout value={value} />
</>
);
}
function Slider({ value, onChange }) {
return (
<input
type="range"
value={value}
onChange={e => onChange(Number(e.target.value))}
/>
);
}
function Readout({ value }) {
return <p>Value is {value}</p>;
} Now both children see the same value, and either one can update
it through the setter passed in.
The Pattern
| Direction | What flows |
|---|---|
| Down (props) | The current value |
| Up (callbacks) | “User just did something — here’s the new value” |
This is sometimes called one-way data flow, and it’s how React encourages you to think about UIs.
How High Do You Lift?
The lowest common parent. Don’t put state at the top of the app just because. If only one branch of the tree cares, that’s where the state belongs.
When Lifting Gets Painful
If you find yourself threading the same state down through 5 layers
of components, each just passing it through, you have prop
drilling. The fix is useContext (covered in chapter 5).
Up Next
A specific kind of lifted state: a form input’s value lives in React, not in the DOM. That’s a controlled input.
Controlled Inputs →