Lifting State Up
Lifting State Up
A teacher selects a class in one panel, but the attendance summary in another panel still shows the previous class. Both panels need to agree about the same choice.
Lifting state up means moving a shared value into the nearest parent that owns all the components needing it. The parent supplies the current value through props and supplies a function that children can call to request a change.
Understand
Find the common owner of a shared class selection.
Build
Connect a selector and summary with props and an event callback.
Check
Explain why two independent copies can disagree.
One class selection, two readers
Parent owns
App remembers the selected class.
Selector requests
A button calls the callback supplied by App.
Both read
The selector and summary receive the same new value.
Find the smallest shared owner
A component can keep private state when no other component needs it. Here the class selector and attendance summary are siblings: neither is the parent of the other. Their common parent is App, so App owns className. This is a question about who needs the value, not about which component appears higher on the screen.
A prop carries information downward. A callback prop carries an invitation to request work: the child calls a function already supplied by its parent. The child does not reach into the parent and assign its variables. Keeping those two directions explicit makes the relationship easier to follow as the school screen grows.
Share a class between sibling panels
This complete App has two small children in the same file. ClassPicker receives the selected class and an onChoose callback. AttendanceSummary only reads className. Their separate markup keeps the responsibilities clear while their shared parent keeps the choice consistent. No Effect is needed to copy values between them.
React · JavaScript with JSX
import { useState } from 'react';
function ClassPicker({ className, onChoose }) {
return <section><p>Selected: {className}</p>
<button onClick={() => onChoose('7B')}>Choose 7B</button>
</section>;
}
function AttendanceSummary({ className }) {
return <p>Attendance for {className}</p>;
}
export default function App() {
const [className, setClassName] = useState('7A');
return <main><h1>Class attendance</h1>
<ClassPicker className={className} onChoose={setClassName} />
<AttendanceSummary className={className} />
</main>;
}Expected screen and behavior
Initially both panels show 7A. Choose 7B updates Selected and Attendance together.Follow one request upward
React · Share a class between sibling panels
import { useState } from 'react';
function ClassPicker({ className, onChoose }) {
return <section><p>Selected: {className}</p>
<button onClick={() => onChoose('7B')}>Choose 7B</button>
</section>;
}
function AttendanceSummary({ className }) {
return <p>Attendance for {className}</p>;
}
export default function App() {
const [className, setClassName] = useState('7A');
return <main><h1>Class attendance</h1>
<ClassPicker className={className} onChoose={setClassName} />
<AttendanceSummary className={className} />
</main>;
}Initially App holds 7A. Its returned JSX passes that string to both children. Clicking Choose 7B invokes the child handler, which calls onChoose with 7B. Because onChoose is setClassName in this App, React schedules a parent update.
The next render of App reads 7B and supplies fresh props. Both child descriptions now use 7B. The original event handler does not immediately rewrite every displayed paragraph; React renders the new description and commits the corresponding changes. The summary never needs its own class state because it has no independent class choice.
Share only what must agree
Lift the class selection, but keep unrelated private details where they belong. For example, a dropdown’s temporary open or closed state can stay in that dropdown if the summary does not need it. Moving every value to the top of an application makes simple components depend on a large parent unnecessarily.
The callback name onChoose describes the child’s request rather than a particular storage method. Later the parent could validate a choice or use a reducer without changing the selector’s basic contract. A read-only summary can receive only a value; it does not need the setter.
Try the school task
React · starting example
import { useState } from 'react';
function ClassPicker({ className, onChoose }) {
return <section><p>Selected: {className}</p>
<button onClick={() => onChoose('7B')}>Choose 7B</button>
</section>;
}
function AttendanceSummary({ className }) {
return <p>Attendance for {className}</p>;
}
export default function App() {
const [className, setClassName] = useState('7A');
return <main><h1>Class attendance</h1>
<ClassPicker className={className} onChoose={setClassName} />
<AttendanceSummary className={className} />
</main>;
}Add a second child showing the timetable for the chosen class. After choosing 7B, the attendance and timetable labels must both say 7B. Do not introduce a second class state variable or synchronize copies with an Effect. Decide where the shared value already lives before changing the markup.
Worked answer
The answer adds Timetable as another reader of the same parent value. Each child still has a narrow job. Checking all three labels after one click demonstrates the useful consequence of a single owner: there is no separate timetable selection to forget to update.
React · JavaScript with JSX
import { useState } from 'react';
function Timetable({ className }) {
return <p>Timetable for {className}</p>;
}
export default function App() {
const [className, setClassName] = useState('7A');
return <main><h1>Shared class</h1><p>Selected: {className}</p>
<button onClick={() => setClassName('7B')}>Choose 7B</button>
<p>Attendance for {className}</p>
<Timetable className={className} />
</main>;
}Expected screen and behavior
Initially all class labels say 7A. Choose 7B makes all three labels say 7B.Mistakes and boundaries
Initializing a child’s own state from className does not keep it synchronized with later props. That initialization supplies a starting value, so the summary could keep displaying 7A. Read the prop directly when the parent is the authority.
Lifting state does not make it permanent. Refreshing this demonstration resets the choice to 7A. Saving class preferences requires a separate persistence decision. Sharing state is also different from passing every object to every child; give each child the information it needs.
Check your reasoning
React · Share a class between sibling panels
import { useState } from 'react';
function ClassPicker({ className, onChoose }) {
return <section><p>Selected: {className}</p>
<button onClick={() => onChoose('7B')}>Choose 7B</button>
</section>;
}
function AttendanceSummary({ className }) {
return <p>Attendance for {className}</p>;
}
export default function App() {
const [className, setClassName] = useState('7A');
return <main><h1>Class attendance</h1>
<ClassPicker className={className} onChoose={setClassName} />
<AttendanceSummary className={className} />
</main>;
}Choose the answer that follows the data and the interaction, then read the explanation for each alternative.
Carry the idea forward
Choose one owner for information that several components must agree about. Values flow down through props, and children request changes through callbacks. Continue with choosing a state structure to avoid storing other duplicate information.
Read the primary reference: React: sharing state between components.
Reaching the recap and completing the quizzes are separate milestones.