Lifting State Up
In this page:
The Problem: Siblings Need Shared Data
When two sibling components each need access to the same piece of state, keeping separate copies in each one causes them to fall out of sync -- updating one won't affect the other. React has no built-in way for sibling components to talk directly to each other.
Note: If you notice two components needing the exact same value, that's the signal to lift the state up to their shared parent.
Warning: Duplicating state across siblings is a common beginner mistake that leads to confusing, inconsistent UI bugs.
Example: The Problem: Siblings Need Shared Data
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function TemperatureDisplay({ celsius }) {
return <p>{celsius}°C is {(celsius * 9/5 + 32).toFixed(1)}°F</p>;
}
function App() {
const [celsius] = React.useState(20);
return <TemperatureDisplay celsius={celsius} />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Moving State to the Common Parent
To fix the sync problem, the state is moved (lifted) up to the closest component that is an ancestor of everything that needs it. That parent then passes the current value down as a prop to each child that displays or uses it.
Note: The parent becomes the single source of truth -- children only receive and display the value, they don't own their own separate copy.
Warning: After lifting state up, remember to remove the now-redundant local state from the child components.
Example: Moving State to the Common Parent
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Display({ value }) { return <p>Current: {value}</p>; }
function App() {
const [value, setValue] = React.useState(0);
return (
<div>
<Display value={value} />
<button onClick={() => setValue(value + 1)}>Increase</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Passing the Updater Down Too
Often a child needs to change the shared state, not just display it. The parent passes its setter function down as a prop, so the child can call it -- keeping the parent as the single owner of the state while still allowing children to trigger updates.
Note: Name the prop for the updater function something clear, like onIncrease, rather than exposing setValue directly.
Warning: Passing the raw setValue function down works, but naming it clearly as an event-like prop (e.g. onChange) makes the component's API easier to understand.
Example: Passing the Updater Down Too
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function IncreaseButton({ onIncrease }) {
return <button onClick={onIncrease}>+1</button>;
}
function Display({ value }) { return <p>Value: {value}</p>; }
function App() {
const [value, setValue] = React.useState(0);
return (
<div>
<Display value={value} />
<IncreaseButton onIncrease={() => setValue(value + 1)} />
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Keeping the same piece of data duplicated in two sibling components' separate state instead of lifting it to their shared parent.
- Forgetting to pass both the value AND the updater function down as props after lifting state up.
- Lifting state higher than necessary, all the way to the top of the app, when a closer common parent would do.
- When two components need the same data, that data should live in their closest common parent.
- The parent passes the value down as a prop, and a function to update it down as another prop.
- This is called 'lifting state up' and is a core React data-flow pattern.
- It avoids duplicated, out-of-sync copies of the same data in sibling components.
No browser-specific restrictions -- a data-flow pattern built on useState and props.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: