As React apps grow in complexity, managing shared state between components can become tricky. Oftentimes, several child components may need to reflect the same data in the UI.
The React solution is to lift the state up to a common ancestor component. The parent component can manage the state, and pass it down to the children via props.
Let’s look at how to lift state for easier data sharing:
The Problem with Local State
Imagine we have a <Toolbox>
component that contains some <Tool>
components:
function Toolbox() {
return (
<div>
<Tool />
<Tool />
<Tool />
</div>
);
}
function Tool() {
// Local state for each tool
const [isActive, setIsActive] = useState(false);
return (
<button onClick={() => setIsActive(!isActive)}>
Tool {isActive ? 'Active' : 'Inactive'}
</button>
);
}
This works at first, but fails once we need to coordinate the tool state. We want to activate one tool at a time.
The local isActive
state in each <Tool>
is independent. We need to lift the state up to the parent <Toolbox>
which can pass the state down.
Lifting State Up into a Parent Component
First, remove the local isActive
state from <Tool>
.
Next, create it in the parent <Toolbox>
instead:
function Toolbox() {
const [activeTool, setActiveTool] = useState(null);
return (
<div>
<Tool
isActive={activeTool === 1}
onClick={() => setActiveTool(1)}
/>
<Tool
isActive={activeTool === 2}
onClick={() => setActiveTool(2)}
/>
<Tool
isActive={activeTool === 3}
onClick={() => setActiveTool(3)}
/>
</div>
);
}
function Tool({isActive, onClick}) {
return (
<button onClick={onClick}>
{isActive ? 'Active' : 'Inactive'}
</button>
);
}
Now the parent <Toolbox>
owns the activeTool state, which it passes down to all <Tool>
components.
Clicking a tool will update the parent state, which will re-render all three tool components with the updated prop.
Benefits of Lifting State
This pattern has several benefits:
- Single source of truth - State is synchronized between components
- Top-down data flow - Parent has full control over state changes
- Better separation of concerns - State logic is isolated in parent
This avoids problems from duplicating state across child components.
Downsides of Lifting State
Lifting state can also introduce complexity:
- More props need to be passed down through the tree
- Parent may become bloated if it manages too much state
- Can make optimization harder
Evaluate tradeoffs before lifting state too high. Find the optimal owner component for each state.
Summary
- Lift shared state up to a common parent component
- Parent component manages state and passes it down through props
- Avoid state inconsistencies by centralizing control
- Balance lifting state with complexity costs
Lifting state helps enforce the uni-directional data flow in React. Mastering this pattern unlocks building complex UIs easily composed of small reusable parts.