ProgramGuru

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.

  1. Understand

    Find the common owner of a shared class selection.

  2. Build

    Connect a selector and summary with props and an event callback.

  3. Check

    Explain why two independent copies can disagree.

One class selection, two readers

  1. Parent owns

    App remembers the selected class.

  2. Selector requests

    A button calls the callback supplied by App.

  3. Both read

    The selector and summary receive the same new value.

Read this relationship alongside the complete code. This diagram explains the flow; it does not execute React.

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.

Why does AttendanceSummary receive className instead of keeping its own selected class?
Choose one answer. You can try again.

Answer and explanation

The summary must agree with the selector, so App owns their one shared selection.

Correct. Both children read the same parent value instead of coordinating two copies.

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.

    Next lesson: Choosing State Structure →