Modificarea stării părintelui în componenta copil în React

În lecția anterioară, starea cu datele era stocată în componenta părinte, iar componentele copil primeau aceste date sub formă de props.

Să presupunem acum că vrem să ne modificăm produsele. Să facem, de exemplu, un buton care va plasa produsul nostru în coș. Pentru început să adăugăm în matricea noastră cu produse câmpul inCart, care indică dacă produsul este în coș sau nu:

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},
];

În componenta Products în tag-ul cu produsul să adăugăm încă un 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>;
}

Să în componenta copil Product facem o afișare a informației despre coș și un buton pentru adăugarea în coș:

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>;
}

Să implementăm adăugarea

Conform regulilor React o componentă nu ar trebui să-și modifice props-urile. Aceasta înseamnă că componenta copil nu se poate pune singură în coș, modificând prop-ul inCart. Aceasta nu este corect.

Corect ar fi să cerem componentei părinte să-și modifice starea prods, punând un anumit produs în coș.

Să vedem cum se face acest lucru.

În componenta-părinte să facem funcția addToCart, care ca parametru primește id-ul produsului și pentru acel produs schimbă proprietatea inCart în true:

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

În tag-ul cu produsul să adăugăm un atribut, în care transmitem funcția creată de noi, precum și un atribut, în care transmitem id-ul produsului:

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

După cum vedeți, în props-urile componentelor se pot transmite nu numai unele date, ci și funcții.

Codul final al clasei Products va fi următorul:

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>;
}

Acum în clasa copil vom avea acces la funcția addToCart. Să apelăm această funcție la click pe buton, transmitându-i ca parametru id-ul produsului:

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>;
}

Se va întâmpla ca la click pe butonul din componenta copil să fie apelată funcția părintelui, care va modifica starea părintelui. Modificarea stării părintelui va declanșa rerandarea și va redesena produsul nostru, transmitându-i prop-ul modificat.

Задание 1

Luați componenta User din lecția anterioară. Faceți astfel încât în ea să apară un buton pentru banarea utilizatorului.