Lesson 38 of 60 – Lifting State Up
63%

Lifting State Up

Lifting State Up is a React pattern used when multiple components need to share the same state. The state is moved from the individual child components to their closest common parent.

Note: After lifting state up, the parent becomes the single source of truth. It can pass the current state and functions for updating that state to its child components through props.

1. What is Lifting State Up?

Lifting state up means moving state from a child component to a common parent component so that multiple components can use the same state.

Before:

Parent
 ├── Child A
 │     └── State
 └── Child B

After:

Parent
 └── State
      ├── Child A
      └── Child B

2. Why Lift State Up?

State should be lifted when two or more components need to stay synchronized.

  • Multiple components need the same data.
  • Components need to respond to the same state changes.
  • Two components need to communicate through shared data.
  • You want one source of truth.
  • You want to avoid duplicate state.

3. Single Source of Truth

After lifting state up, the common parent becomes the single source of truth for that piece of state.

          Parent
        Shared State
          /      \
         /        \
     Child A    Child B

Both children receive information from the same state.

4. Example Without Lifting State

Suppose two components each maintain their own temperature value.

function Celsius() {

    const [temperature, setTemperature] =
        useState("");

}

function Fahrenheit() {

    const [temperature, setTemperature] =
        useState("");

}

These are two separate states. Changing one does not automatically change the other.

5. The Problem with Separate State

If two components represent the same information but keep separate state values, they can become inconsistent.

Temperature A = 25
Temperature B = 20

The application now has two different values for information that should represent the same temperature.

6. Moving State to the Parent

The solution is to move the state into the closest common parent.

function TemperatureApp() {

    const [temperature, setTemperature] =
        useState("");

    return (
        <div>

            <CelsiusInput
                temperature={temperature}
                setTemperature={setTemperature}
            />

            <FahrenheitInput
                temperature={temperature}
                setTemperature={setTemperature}
            />

        </div>
    );
}

7. Passing State as Props

The parent can pass its state to a child through props.

<Child
    value={value}
/>

The child can then use the value.

function Child({ value }) {

    return (
        <p>
            Value: {value}
        </p>
    );
}

8. Passing the State Setter

If the child needs to change the shared state, the parent can pass a setter function.

<Child
    value={value}
    setValue={setValue}
/>

The child can then call setValue.

9. Child Updating Parent State

function Child({
    value,
    setValue
}) {

    return (
        <button
            onClick={() =>
                setValue(value + 1)
            }
        >
            Value: {value}
        </button>
    );
}

The child does not own the state. It requests a change to the state owned by the parent.

10. Lifting State Between Siblings

Sibling components cannot directly pass state to each other.

        Parent
        /    \
       /      \
  Child A   Child B

The parent can hold the state and pass data down to both siblings.

11. Sibling Communication

Suppose Child A changes a value and Child B needs to display it.

Child A
   |
   | update
   ↓
Parent State
   |
   | value
   ↓
Child B

This is one of the most common reasons for lifting state up.

12. Basic Sibling Example

function App() {

    const [message, setMessage] =
        useState("");

    return (
        <div>

            <Input
                setMessage={setMessage}
            />

            <Display
                message={message}
            />

        </div>
    );
}

Input changes the state while Display reads the same state.

13. Input Component

function Input({ setMessage }) {

    return (
        <input
            onChange={e =>
                setMessage(
                    e.target.value
                )
            }
            placeholder="Type something"
        />
    );
}

14. Display Component

function Display({ message }) {

    return (
        <h2>
            {message}
        </h2>
    );
}

Whenever the parent state changes, the Display component receives the updated value.

15. Lifting State in Forms

Forms are a common situation where state needs to be shared between components.

function App() {

    const [name, setName] =
        useState("");

    return (
        <div>

            <NameInput
                name={name}
                setName={setName}
            />

            <Preview
                name={name}
            />

        </div>
    );
}

16. Controlled Input After Lifting State

function NameInput({
    name,
    setName
}) {

    return (
        <input
            value={name}
            onChange={e =>
                setName(
                    e.target.value
                )
            }
        />
    );
}

The parent owns the value while the input receives and updates it through props.

17. Preview Component

function Preview({ name }) {

    return (
        <div>

            <h2>
                Preview
            </h2>

            <p>
                Hello {name}
            </p>

        </div>
    );
}

The Preview component receives the same state that controls the input.

18. Lifting State with Multiple Children

function App() {

    const [selected, setSelected] =
        useState(null);

    return (
        <div>

            <StudentList
                setSelected={setSelected}
            />

            <StudentDetails
                selected={selected}
            />

        </div>
    );
}

One child updates the state while another child uses the updated value.

19. Passing Callback Functions

Instead of passing a state setter directly, the parent can define a callback function and pass it to the child.

function App() {

    const [selected, setSelected] =
        useState(null);

    function handleSelect(id) {

        setSelected(id);

    }

    return (
        <StudentList
            onSelect={handleSelect}
        />
    );
}

20. Child Calling the Callback

function StudentList({
    onSelect
}) {

    return (
        <button
            onClick={() =>
                onSelect(101)
            }
        >
            Select Student
        </button>
    );
}

The child calls the callback, and the parent updates its state.

21. Temperature Converter Example

A temperature converter is a classic example of lifting state up.

function Calculator() {

    const [temperature, setTemperature] =
        useState("");

    return (
        <div>

            <TemperatureInput
                temperature={temperature}
                setTemperature={setTemperature}
            />

            <TemperatureResult
                temperature={temperature}
            />

        </div>
    );
}

22. Shopping Cart Example

A shopping cart can be owned by a parent while product and cart components use the shared state.

function App() {

    const [cart, setCart] =
        useState([]);

    return (
        <div>

            <ProductList
                setCart={setCart}
            />

            <Cart
                cart={cart}
            />

        </div>
    );
}

23. Lifting State and Derived Data

A parent can keep the main state and derive other values from it.

const [items, setItems] =
    useState([]);

const totalItems =
    items.length;

const totalPrice =
    items.reduce(
        (total, item) =>
            total + item.price,
        0
    );

Child components can receive these derived values as props.

24. Lifting State vs Context

Lifting State Context
Moves state to a common parent. Provides values through a Context.
Usually works well for nearby components. Useful for values needed by distant descendants.
Data is passed through props. Components can read the Context value.
Simple and explicit. Can reduce prop drilling.

25. Lifting State vs Duplicate State

Lifting State Duplicate State
One source of truth. Multiple copies of related data.
Components remain synchronized. Values can become inconsistent.
Updates are centralized. Updates must be synchronized manually.

26. When Should You Lift State?

Consider lifting state when:

  • Two sibling components need the same state.
  • One component changes data that another component displays.
  • Multiple components need to stay synchronized.
  • You have duplicate state representing the same information.
  • You need a single source of truth.

27. When Should You Not Lift State?

Not every piece of state needs to be moved upward.

  • If only one component uses the state, keep it there.
  • Avoid moving state unnecessarily high in the component tree.
  • Do not create global state for local component data.
  • Keep state as close as possible to the components that need it.

The goal is to find the appropriate owner for each piece of state.

28. Common Lifting State Mistakes

  • Keeping duplicate copies of the same state.
  • Lifting state higher than necessary.
  • Passing unnecessary props through many components.
  • Mutating shared state directly.
  • Forgetting to pass the update callback.
  • Using Context when simple props are enough.
  • Making one parent responsible for unrelated state.

29. Complete Lifting State Example

import { useState } from "react";

function App() {

    const [name, setName] =
        useState("");

    return (
        <div>

            <NameInput
                name={name}
                setName={setName}
            />

            <NamePreview
                name={name}
            />

        </div>
    );
}

function NameInput({
    name,
    setName
}) {

    return (
        <input
            value={name}
            onChange={e =>
                setName(
                    e.target.value
                )
            }
            placeholder="Enter name"
        />
    );
}

function NamePreview({ name }) {

    return (
        <div>

            <h2>
                Preview
            </h2>

            <p>
                Name: {name}
            </p>

        </div>
    );
}

export default App;

The state is stored in App. NameInput changes it, and NamePreview displays the same state.

30. Lifting State Best Practices

  • Keep one source of truth for shared data.
  • Lift state to the closest common parent.
  • Do not lift state higher than necessary.
  • Pass only required values through props.
  • Pass callback functions when children need to update state.
  • Use functional state updates when the next state depends on the previous state.
  • Avoid duplicate state representing the same information.
  • Use Context when state must be accessed by many distant components.
  • Use useReducer when state transitions become complex.
  • Keep state ownership clear and predictable.

📌 Key Points

  • Lifting state up means moving state to a common parent.
  • The parent becomes the single source of truth.
  • Child components receive shared state through props.
  • Children can request updates using setter functions or callbacks.
  • Lifting state is especially useful for sibling components.
  • It prevents unnecessary duplicate state.
  • State should be lifted only as high as necessary.
  • Context can be considered when many distant components need shared data.
  • useReducer can help manage complex shared state logic.
  • The goal is to keep related components synchronized with a clear state owner.

🧠 Quick Quiz

Question: What does "lifting state up" mean in React?