In React, sharing state means allowing multiple components to use and respond to the same state. When two or more components need the same changing information, the state can be moved to their common parent and then passed to them through props.
Sharing state means making one piece of state available to multiple components that need to work with the same data.
Parent
├── Component A
└── Component B
If both components need the same state, the parent can own the state and pass the required values and functions to its children.
Sharing state helps different parts of an application stay synchronized.
When multiple components need the same state, it is often better to keep one source of truth instead of maintaining separate copies.
Parent State
|
+---- Child A
|
+---- Child B
Both components can receive the current value from the same parent state.
The simplest way to share state between related components is through props.
function Parent() {
const [name, setName] =
useState("Rahul");
return (
<Child name={name} />
);
}
The child receives the state value through a prop.
A parent can pass both the state value and its setter function.
function Parent() {
const [name, setName] =
useState("Rahul");
return (
<Child
name={name}
setName={setName}
/>
);
}
The child can then request changes through the setter.
function Child({ name }) {
return (
<h2>
Student: {name}
</h2>
);
}
The child does not create another copy of the name. It uses the value provided by the parent.
function Child({
name,
setName
}) {
return (
<button
onClick={() =>
setName("Amit")
}
>
Change Name
</button>
);
}
The child can update the parent's state by using the setter function passed through props.
function Parent() {
const [count, setCount] =
useState(0);
return (
<div>
<Display count={count} />
<Counter
setCount={setCount}
/>
</div>
);
}
The Display component reads the state while Counter changes it.
function Display({ count }) {
return (
<h2>
Count: {count}
</h2>
);
}
This component only displays the shared state.
function Counter({ setCount }) {
return (
<button
onClick={() =>
setCount(
previous =>
previous + 1
)
}
>
Increase
</button>
);
}
The Counter component changes the state owned by its parent.
Sibling components cannot directly share state with each other. Their common parent can own the state and pass it to both.
Parent
/ \
/ \
Child A Child B
The parent becomes the common source of the shared state.
function Parent() {
const [message, setMessage] =
useState("");
return (
<div>
<Input
setMessage={setMessage}
/>
<Display
message={message}
/>
</div>
);
}
Input changes the shared state, while Display reads the same state.
function Input({ setMessage }) {
return (
<input
onChange={e =>
setMessage(
e.target.value
)
}
placeholder="Type message"
/>
);
}
function Display({ message }) {
return (
<h2>
Message: {message}
</h2>
);
}
The Display component automatically receives the latest state through its prop when the parent re-renders.
Forms often use shared state to keep input values synchronized with other parts of the interface.
function Parent() {
const [email, setEmail] =
useState("");
return (
<div>
<EmailInput
email={email}
setEmail={setEmail}
/>
<p>
Email: {email}
</p>
</div>
);
}
A parent can share an object containing several related values.
const [student, setStudent] =
useState({
name: "Rahul",
course: "React",
age: 20
});
Child components can receive the complete object or only the properties they need.
setStudent({
...student,
course: "JavaScript"
});
The spread operator keeps the existing properties while changing the selected property.
A parent does not always need to pass the complete state object.
<StudentName
name={student.name}
/>
<StudentCourse
course={student.course}
/>
Passing only required values can make component interfaces clearer.
A parent can provide callback functions that allow child components to request state changes.
function Parent() {
const [selected, setSelected] =
useState(null);
function selectStudent(id) {
setSelected(id);
}
return (
<StudentList
onSelect={selectStudent}
/>
);
}
A function passed from a parent to a child is commonly called a callback prop.
<Child
onSelect={handleSelect}
/>
The child can call the function when an event occurs.
function Child({ onSelect }) {
return (
<button
onClick={() =>
onSelect(10)
}
>
Select
</button>
);
}
A shopping application may have a cart state that needs to be used by several components.
function App() {
const [cart, setCart] =
useState([]);
return (
<div>
<ProductList
setCart={setCart}
/>
<Cart
cart={cart}
/>
</div>
);
}
ProductList can add products while Cart displays the shared cart.
function App() {
const [search, setSearch] =
useState("");
return (
<div>
<SearchBox
search={search}
setSearch={setSearch}
/>
<Results
search={search}
/>
</div>
);
}
Both SearchBox and Results work with the same search state.
A tab interface can keep the selected tab in a parent component.
const [activeTab, setActiveTab] =
useState("home");
return (
<div>
<Tabs
activeTab={activeTab}
setActiveTab={setActiveTab}
/>
<TabContent
activeTab={activeTab}
/>
</div>
);
| Shared State | Duplicate State |
|---|---|
| One source of truth. | Multiple independent copies. |
| Components stay synchronized. | Copies can become inconsistent. |
| Updates can be coordinated. | More synchronization may be required. |
| Useful when components depend on the same data. | Useful when values are intentionally independent. |
For state that needs to be accessed by many components at different levels of the component tree, Context can be useful.
const UserContext =
createContext(null);
<UserContext.Provider
value={user}
>
<App />
</UserContext.Provider>
Components can then use useContext to read the shared
value.
When shared state has complex update logic,
useReducer can organize state transitions.
const [
state,
dispatch
] = useReducer(
reducer,
initialState
);
The state and dispatch function can then be passed to child components or provided through Context.
A useful approach is to keep state as close as possible to the components that need it.
import { useState } from "react";
function App() {
const [count, setCount] =
useState(0);
return (
<div>
<Display
count={count}
/>
<Counter
setCount={setCount}
/>
</div>
);
}
function Display({ count }) {
return (
<h2>
Count: {count}
</h2>
);
}
function Counter({ setCount }) {
function increase() {
setCount(
previous =>
previous + 1
);
}
function decrease() {
setCount(
previous =>
previous - 1
);
}
return (
<div>
<button onClick={increase}>
Increase
</button>
<button onClick={decrease}>
Decrease
</button>
</div>
);
}
export default App;
Here, the parent owns the state. Display reads the state and Counter updates the same state.
Question: What is a common way to share state between sibling components in React?