Změna stavu rodiče v dětské komponentě v Reactu

V předchozí lekci jsme měli stav s daty uložený v rodičovské komponentě a dětské komponenty tato data získávaly jako props.

Nyní předpokládejme, že chceme naše produkty měnit. Vytvořme například tlačítko, které bude přidávat náš produkt do košíku. Pro začátek přidejme do našeho pole s produkty pole inCart, které ukazuje, zda je produkt v košíku nebo ne:

jsxconst initProds = [
  {id: id(), name: 'product1', cost: 100, inCart: false},
  {id: id(), name: 'product2', cost: 200, inCart: false},
  {id: id(), name: 'product3', cost: 300, inCart: false},
];

V komponentě Products přidejme do tagu s produktem ještě jeden atribut inCart:

jsxfunction Products() {
  const [prods, setProds] = useState(initProds);
  
  const items = prods.map(prod => {
    return <Product
      key   ={prod.id}
      name  ={prod.name}
      cost  ={prod.cost}
      inCart={prod.inCart}
    />;
  });
  
  return <div>
    {items}
  </div>;
}

V dětské komponentě Product udělejme výpis informace o košíku a tlačítko pro přidání do košíku:

jsxfunction Product({ id, name, cost, inCart }) {
  return <div>
    name: <span>{name}</span>,
    cost: <span>{cost}</span>,
    <span>{inCart ? 'in cart' : 'not in cart'}</span>
    <button>to cart</button>
  </div>;
}

Implementujeme přidání

Podle pravidel Reactu komponenta by neměla měnit své props. To znamená, že dětská komponenta nemůže sama sebe přidat do košíku změnou props inCart. To není správně.

Správně je požádat rodičovskou komponentu, aby změnila svůj stav prods a přidala daný produkt do košíku.

Podívejme se, jak se to dělá.

V komponentě-rodiči vytvořme funkci addToCart, která parametrem přijímá id produktu a pro tento produkt mění vlastnost inCart na true:

jsxfunction addToCart(id) {
  setProds(prods.map(prod => {
    if (prod.id === id) {
      prod.inCart = true;
    }
    
    return prod;
  }));
}

Do tagu s produktem přidejme atribut, do kterého předáme naši vytvořenou funkci, a také atribut, do kterého předáme id produktu:

jsxconst items = prods.map(prod => {
  return <Product
    key      ={prod.id}
    id       ={prod.id}
    name     ={prod.name}
    cost     ={prod.cost}
    inCart   ={prod.inCart}
    addToCart={addToCart}
  />;
});

Jak vidíte, do props komponent lze předávat nejen nějaká data, ale i funkce.

Výsledný kód třídy Products bude vypadat následovně:

jsxfunction Products() {
  const [prods, setProds] = useState(initProds);
  
  function addToCart(id) {
    setProds(prods.map(prod => {
      if (prod.id === id) {
        prod.inCart = true;
      }
      
      return prod;
    }));
  }
  
  const items = prods.map(prod => {
    return <Product
      key      ={prod.id}
      id       ={prod.id}
      name     ={prod.name}
      cost     ={prod.cost}
      inCart   ={prod.inCart}
      addToCart={addToCart}
    />;
  });
  
  return <div>
    {items}
  </div>;
}

Nyní bude v dětské třídě dostupná funkce addToCart. Zavolejme tuto funkci po kliknutí na tlačítko a předejme jí parametrem id produktu:

jsxfunction Product({ id, name, cost, inCart, addToCart }) {
  return <div>
    name: <span>{name}</span>,
    cost: <span>{cost}</span>,
    <span>{inCart ? 'in cart' : 'not in cart'}</span>
    <button onClick={() => addToCart(id)}>to cart</button>
  </div>;
}

Stane se, že po kliknutí na tlačítko v potomkovi se zavolá funkce rodiče, která změní stav rodiče. Změna stavu rodiče způsobí překreslení a překreslí náš produkt a předá mu změněný props.

Задание 1

Vezměte komponentu User z předchozí lekce. Udělejte to, aby se v něm objevilo tlačítko pro zabanování uživatele.