Lifting State Up

The festival app is made of components now: a Header at the top, and a BandCard for each band. Each card has a star button, and each card keeps in its own state whether its band is a favorite, as you saw in the State lesson. Now the header should say how many favorites there are. But it can't: the state is inside the cards, and a component can't read the state of another one. Props only go one way, down from a parent to its children.

When two components need the same state, move it up to their closest common parent: the nearest component that has both of them inside it. Here, that's App. The state lives there, and App passes each child what it needs as props: the value, and a function that changes it. This is called lifting state up.

A small example, with two components in one file: the cart shows the tickets you've bought, and a button below it buys one more:

import { useState } from "react";

export default function App() {
    const [tickets, setTickets] = useState(0);

    function handleBuy() {
        setTickets((tickets) => tickets + 1);
    }

    return (
        <main>
            <Cart tickets={tickets} />
            <BuyButton onBuy={handleBuy} />
        </main>
    );
}

function Cart({ tickets }) {
    return <p>Tickets in your cart: {tickets}</p>;
}

function BuyButton({ onBuy }) {
    return <button onClick={onBuy}>Buy a ticket</button>;
}

BuyButton doesn't know what buying does. It only calls onBuy when it's clicked. App decides what happens, changes its state and renders again, so Cart gets the new number in its props. The state is in one place, and both children use the same value.

For the favorites, App keeps an array of the ids of the favorite bands. Each card gets two props: favorite, whether its band is in the array, and onToggleFavorite, a function that adds the band to the array or takes it out:

function handleToggleFavorite(id) {
    if (favorites.includes(id)) {
        setFavorites((favorites) =>
            favorites.filter((favoriteId) => favoriteId !== id),
        );
    } else {
        setFavorites((favorites) => [...favorites, id]);
    }
}

onToggleFavorite={() => handleToggleFavorite(band.id)} gives each card a function that already knows its band, as in the Events lesson. And the card doesn't need useState any more: it shows what its props say, and calls onToggleFavorite when its star is clicked.

Instructions

In App, keep the favorites in a state variable favorites: an array of ids that starts empty. Give each BandCard the props favorite and onToggleFavorite, and in BandCard.jsx, use them instead of the card's own state.

Pass Header the number of favorites as count. Star a few bands to try it.

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

Back

1/6

Next