Lifting State Up and Data Flow
In React, state is local to the component that declares it. When two or more sibling components need to access or modify the same state, you must move the state up to their nearest common ancestor component. This is called Lifting State Up.
1. The Problem: Uncoordinated Sibling State
Imagine an application with two inputs: one for temperature in Celsius and another in Fahrenheit. If each input component manages its own state locally, keeping them synchronized is difficult:
// Bad Approach: Independent states
function CelsiusInput() {
const [celsius, setCelsius] = useState("");
return <input value={celsius} onChange={e => setCelsius(e.target.value)} />;
}
function FahrenheitInput() {
const [fahrenheit, setFahrenheit] = useState("");
return <input value={fahrenheit} onChange={e => setFahrenheit(e.target.value)} />;
}2. The Solution: Lifting State to the Common Ancestor
To keep these inputs in sync, we lift the state up to the parent component. The parent component will manage the state and pass the values and update callback handlers down to the child inputs as props.
Here is the implementation:
import { useState } from "react";
// Child 1: Reads value and triggers update callbacks
function TemperatureInput({ scale, temperature, onTemperatureChange }) {
return (
<fieldset>
<legend>Enter temperature in {scale}:</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
}
// Parent: Manages the source of truth and calculations
function Calculator() {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
const handleCelsiusChange = (val) => {
setTemperature(val);
setScale("c");
};
const handleFahrenheitChange = (val) => {
setTemperature(val);
setScale("f");
};
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>
);
}3. Benefits of Unidirectional Data Flow
Lifting state up reinforces React One-Way Data Flow model:
- Single Source of Truth: The state resides in a single component, preventing sync bugs.
- Predictable Debugging: If a bug occurs, you only need to inspect the component that owns the state and provides the update callbacks.
- Component Reusability: Child components become stateless presentation wrappers, making them easier to test and reuse.
Published on Last updated: