How to Update Lifted State in React
Sharing state between components in React often requires “lifting state up” to their closest common ancestor. This article explains how to lift state up, how to update that lifted state from child components using callback functions, and when to consider alternative state management solutions.
The Concept of Lifting State Up
In React, data flows downward (unidirectional data flow). When two or more sibling components need access to the same changing state, you must move that state up to their nearest common parent component. The parent then passes the state down to the children as read-only properties (props).
To allow child components to update this state, the parent must also pass down a state-updating function (a callback) as a prop.
Step-by-Step: How to Update Lifted State
Updating lifted state involves three main steps: defining the state in the parent, passing the state and updater function to the children, and triggering the updater function from the child.
1. Define State and Handler in the Parent Component
First, declare the state in the common parent component using the
useState hook. Next, create a handler function that will
update this state when called.
import { useState } from 'react';
function ParentComponent() {
const [sharedText, setSharedText] = useState('');
const handleTextChange = (newValue) => {
setSharedText(newValue);
};
return (
<div>
<InputComponent value={sharedText} onValueChange={handleTextChange} />
<DisplayComponent value={sharedText} />
</div>
);
}2. Pass State and Handlers as Props
In the example above, the parent component passes: * The current
state (sharedText) to DisplayComponent to be
rendered. * The current state (sharedText) and the updater
function (handleTextChange) to
InputComponent.
3. Trigger the Update from the Child Component
The child component receiving the updater function can trigger it in
response to user interactions, such as an onChange or
onClick event.
function InputComponent({ value, onValueChange }) {
return (
<input
type="text"
value={value}
onChange={(e) => onValueChange(e.target.value)}
placeholder="Type something..."
/>
);
}
function DisplayComponent({ value }) {
return <p>The current value is: {value}</p>;
}When the user types into the input field, the onChange
event fires, executing onValueChange. This runs the
handleTextChange function in the parent component, updating
the state. The parent re-renders, passing the newly updated value down
to both child components.
Best Practices and Alternatives
While lifting state up is a fundamental React pattern, keeping your application maintainable requires following a few best practices:
- Keep State Local When Possible: Only lift state as high as necessary. If only one component needs a piece of state, keep it inside that component.
- Avoid Prop Drilling: If you find yourself passing state and updater functions down through multiple layers of intermediate components that do not use the data themselves, you are “prop drilling.”
- Use React Context: For deeply nested component trees or global state (like user authentication or UI themes), use the React Context API or state management libraries like Redux or Zustand instead of lifting state.