Vanhemman komponentin tilan muuttaminen lapsikomponentissa Reactissa

Edellisessä oppitunnissa tietotila sijoitettiin vanhemman komponentin sisään, ja lapsikomponentit saivat nämä tiedot propseina.

Oletetaan nyt, että haluamme muuttaa tuotteitamme. Tehdään esimerkiksi painike, joka laittaa tuotemme ostoskoriin. Aluksi lisätään tuotematriisiimme kenttä inCart, joka näyttää, onko tuote ostoskorissa vai ei:

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

Komponentissa Products tuotteen tagiin lisätään toinen attribuutti 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>;
}

Tehdään lapsikomponentissa Product ostoskoritiedon tulostus ja painike ostoskoriin lisäämiseksi:

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

Toteutetaan lisäys

Reactin sääntojen mukaan komponentin ei pitäisi muuttaa sen propseja. Tämä tarkoittaa, että lapsikomponentti ei voi laittaa itse itseään ostoskoriin muuttamalla propsia inCart. Tämä on väärin.

Oikea tapa on pyytää vanhempakomponenttia muuttamaan sen tilaa prods laittamalla tietty tuote ostoskoriin.

Katsotaan kuinka se tehdään.

Vanhemmassakomponentissa tehdään funktio addToCart, joka parametrina ottaa tuotteen id ja muuttaa kyseisen tuotteen ominaisuuden inCart arvoksi true:

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

Tuotteen tagiin lisätään attribuutti, johon annetaan luomamme funktio, sekä attribuutti, johon annetaan tuotteen 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}
  />;
});

Kuten näette, komponenttien propseihin voidaan antaa ei vain tietoja, vaan myös funktioita.

Lopullinen Products-luokan koodi tulee olemaan seuraava:

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

Nyt lapsiluokassa meillä on käytettävissä funktio addToCart. Kutsutaan tätä funktiota painikkeen klikkauksella, antamalla sille parametrina tuotteen 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>;
}

Käy niin, että kun painiketta napsautetaan jälkikasvussa, kutsutaan vanhemman funktiota, joka muuttaa vanhemman tilan. Vanhemman tilan muuttaminen aiheuttaa uudelleenrenderöinnin ja piirtää uudelleen tuotteemme, antaen sille muutetun propin.

Задание 1

Ota komponentti User edellisestä oppitunnista. Tee niin, että siihen ilmestyy painike käyttäjän estämiseksi.