Kódím.cz
6

State lifting

Naučíme se techniku přesouvání stavu nahoru ve stromě komponent

Controlled komponenty

Pokud nějaké komponenta, například CartItem, spravuje stav jiné komponenty, například Amount, říkáme, že komponenta Amount je takzvaně controlledkontrolovaná. Pokud naopak komponenta spravuje svůj vlastní stav, říkáme, že je uncontrollednekontrolovaná.

V našem příkladu z předchozí lekce jsme tedy udělali z komonenty Amount kontrolovanou komponentu. K vyřešení našeho problému to ale nestačí. Potřebujeme stav povýšit až do komponenty Cart. To znamená, že komponenta Cart bude muset spravovat stav pro všechny produkty najednou. Máme však štěstí, protože každý produkt v poli produktů už obsahuje položku amount. Stačí tedy toto pole použít jako stav. Můžeme jej pojmenovat třeba cartProducts.

Nejprve upravíme komponentu CartItem tak, aby už nepoužívala stav count.

interface ICartItemProps {
  product: IProduct;
  onAmountChange: (newCount: number) => void;
}

const CartItem: React.FC<ICartItemProps> = ({ product, onAmountChange }) => {
  return (
    <div className="cart-item">
      <CartProduct name={product.name} price={product.price} />
      <Amount value={product.amount} onChange={onAmountChange} />
    </div>
  )
};

Všimněte si callbacku onAmountChange, který potřebujeme, abychom se dozvěděli o aktualizacích hodnoty uvnitř komponenty Amount.

Nyní si pouze musíme dát dobrý pozor na správnou aktulizaci stavu cartProducts.

const Cart : React.FC = () => {
  const [cartProducts, setCartProducts] = useState(products);

  const handleAmountChange = (index: number, amount: number) => {
    const newProducts = [...cartProducts];
    newProducts[index].amount = amount;
    setCartProducts(newProducts);
  }

  return (
    <div className="cart">
      <div className="cart__head">
        <h2>Košík</h2>
        <span>Položek: 1</span>
      </div>
      <div className="cart__items">
        {cartProducts.map((product, index) => (
          <CartItem 
            product={product} 
            onAmountChange={amount => handleAmountChange(index, amount)} 
            key={product.name} />
        ))}
      </div>
    </div>
  )
};

Konečně jsme ve fázi, kdy můžeme spočítat celkový počet položek v košíku. Výpočet provedeme přímo v těle komponenty Cart, aby se aktualizoval s každou změnou stavu.

  let productCount = 0;
  cartProducts.forEach((product) => productCount += product.amount);

  return (
    <div className="cart">
      <div className="cart__head">
        <h2>Košík</h2>
        <span>Položek: {productCount}</span>