Промена стања родитеља у дочерном компоненту у 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 из претходног урока. Направите тако да у њој се појави дугме за банирање корисника.