Spreminjanje stanja starša v podrejenem komponentu v React

V prejšnji lekciji je bilo stanje s podatki shranjeno v nadrejenem komponentu, podrejeni komponenti pa so te podatke prejemali v obliki propov.

Recimo, da zdaj želimo spreminjati naše produkte. Naredimo na primer gumb, ki bo dodal naš produkt v košarico. Za začetek dodajmo v naš array s produkti polje inCart, ki prikazuje, ali je produkt v košarici ali 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 komponento Products v tag z izdelkom dodajmo še en 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 podrejenem komponentu Product naredimo izpis informacije o košarici in gumb za dodajanje v košarico:

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

Implementirajmo dodajanje

Po pravilih React komponenta ne sme spreminjati svojih propov. To pomeni, da podrejeni komponent ne more sam sebe dati v košarico s spreminjanjem propa inCart. To ni pravilno.

Pravilno je, da prosimo nadrejeni komponent, da spremeni svoje stanje prods in določen izdelek doda v košarico.

Poglejmo, kako se to naredi.

V komponenti-staršu naredimo funkcijo addToCart, ki kot parameter sprejme id izdelka in za ta izdelek spremeni lastnost inCart v true:

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

V tag z izdelkom dodajmo atribut, v katerega posredujemo ustvarjeno funkcijo, in atribut, v katerega posredujemo id izdelka:

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

Kot lahko vidite, lahko v prope komponent posredujete ne le nekaj podatkov, ampak tudi funkcije.

Končna koda razreda Products bo naslednja:

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

Zdaj bo v podrejenem razredu na voljo funkcija addToCart. Pokličimo to funkcijo ob kliku na gumb in ji posredujmo kot parameter id izdelka:

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

Izkazalo se bo, da bo ob kliku na gumb v potomcu poklicala funkcija starša, ki bo spremenila stanje starša. Spreminjanje stanja starša bo povzročilo ponovno upodabljanje in ponovno risanje našega izdelka, pri čemer mu bo posredoval spremenjen prop.

Задание 1

Vzemite komponento User iz prejšnje lekcije. Naredite tako, da se v njej pojavi gumb za ban uporabnika.