Змяненне стэйту бацькі ў даччыным кампаненце ў React

У папярэднім уроке ў нас стэйт з данымі захоўваўся ў бацькоўскім кампаненце, а даччыныя кампаненты атрымлівалі гэтыя даныя ў выглядзе пропсаў.

Хай цяпер мы хочам змяняць нашы прадукты. Зробім, да прыкладу, кнопку, якая будзе змяшчаць наш прадукт у кошык. Для пачатку дадаймо ў наш масіў з прадуктамі поле 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 з папярэдняга ўрока. Зрабіце так, каб у ім з'явілася кнопка для бана юзера.