Промена на состојбата на родителот во детски компонент во React

Во претходната лекција, состојбата со податоци се чуваше во родителскиот компонент, а детските компоненти ги добиваа тие податоци во форма на пропси.

Сега да речеме дека сакаме да ги менуваме нашите производи. Да направиме, на пример, копче што ќе го става нашиот производ во кошничката. За почеток ајде да додадеме во нашиот производен array поле inCart, кое покажува дали производот е во кошничка или не:

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

Во компонентот Products во тагот со производот додадеме уште еден атрибут 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>;
}

Ајде во детскиот компонент Product да направиме приказ на информации за кошничката и копче за додавање во кошничката:

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

Го имплементираме додавањето

Според правилата на React компонентата не треба да ги менува своите пропси. Ова значи дека детската компонента не може сама да се стави во кошничката, менувајќи го пропсот inCart. Ова не е точно.

Правилно ќе биде да се побара од родителската компонента да ја смени својата состојба prods, ставајќи одреден производ во кошничката.

Ајде да погледнеме како се прави ова.

Во компонентата-родител да направиме функција addToCart, која како параметар прима id на производот и за тој производ го менува својството inCart на true:

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

Во тагот со производот да додадеме атрибут, во кој ќе ја пренесеме креираната од нас функција, како и атрибут, во кој ќе го пренесеме id на производот:

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

Како што гледате, во пропсите на компонентите може да се пренесуваат не само некои податоци, туку и функции.

Крајниот код на класата Products ќе излезе следниов:

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

Сега во детската класа ќе ни биде достапна функцијата addToCart. Да ја повикаме оваа функција при клик на копчето, пренесувајќи и како параметар id на производот:

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

Ќе испадне дека при клик на копчето во потомокот ќе се повика функцијата на родителот, која ќе ја промени родителската состојба. Промената на родителската состојба ќе предизвика пререндерирање и ќе го прецрта нашиот производ, пренесувајќи му го променетиот пропс.

Задание 1

Земете ја компонентата User од претходната лекција. Направете така што во неа ќе се појави копче за банирање на корисникот.