Lifting State Up
Lifting State Up
“Lifting State Up” is a fundamental pattern in React. It is the process of moving state from a child component to a common parent component so that multiple children can share and synchronize that state.
React data flow is strictly one-way (top-down). Siblings cannot pass data directly to each other. If Sibling A and Sibling B both need access to the same piece of changing data, that data must live in their Parent.
The Problem
Imagine an application where you have two separate components: one that calculates temperature in Celsius, and one in Fahrenheit.
If they manage their own local state, they fall out of sync immediately.
// ❌ Out of sync
function CelsiusInput() {
const [temp, setTemp] = useState('');
return <input value={temp} onChange={e => setTemp(e.target.value)} />
}
function FahrenheitInput() {
const [temp, setTemp] = useState('');
return <input value={temp} onChange={e => setTemp(e.target.value)} />
}
function Calculator() {
return (
<div>
<CelsiusInput />
<FahrenheitInput />
</div>
)
}
If you type “100” into the Celsius input, the Fahrenheit input knows nothing about it.
The Solution: Lifting State Up
To fix this, we remove the useState from both child components. We “lift” that state up into the Calculator component.
The Calculator will hold the “single source of truth.” It will pass the current temperature down to the inputs as a prop, and it will pass a function down so the inputs can update the parent’s state.
// ✅ Synchronized
function TemperatureInput({ scale, temperature, onTemperatureChange }) {
// This component has NO state. It is fully controlled by its parent.
return (
<label>
Enter temperature in {scale}:
<input
value={temperature}
onChange={e => onTemperatureChange(e.target.value)}
/>
</label>
);
}
function Calculator() {
// The state lives here now!
const [temperature, setTemperature] = useState('');
const [scale, setScale] = useState('c');
const handleCelsiusChange = (temp) => {
setScale('c');
setTemperature(temp);
};
const handleFahrenheitChange = (temp) => {
setScale('f');
setTemperature(temp);
};
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="Celsius"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="Fahrenheit"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
</div>
);
}
When to Lift State Up
You should lift state up whenever you notice that two components need to reflect the same changing data.
Do not lift state up unnecessarily. If you lift state too high up the component tree (e.g., all the way to App.js just to pass it down 5 levels), you create “Prop Drilling.” If state needs to be shared across many deeply nested components, you should consider using the Context API or a global state manager instead.
Interview Questions
Q: When should you “lift state up”?
A: You should lift state up when two or more sibling components need to access, share, or synchronize the exact same piece of data. You move the state to their closest common ancestor, and pass the state (and the setter function) down to the siblings via props.