Endre forelderkomponentens state i en barnekomponent i React

I forrige leksjon ble staten med data lagret i forelderkomponenten, og barnekomponentene mottok disse dataene som props.

La oss nå si at vi ønsker å endre våre produkter. La oss for eksempel lage en knapp som vil plassere produktet vårt i handlekurven. Til å begynne med la oss legge til et felt inCart i produktmatrisen vår som viser om produktet er i handlekurven eller ikke:

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

I komponenten Products, la oss legge til enda et attributt inCart i taggen med produktet:

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

La oss i barnekomponenten Product lage en utskrift av informasjon om handlekurven og en knapp for å legge til i handlekurven:

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

Implementerer tillegget

I følge Reacts regler skal en komponent ikke endre sine props. Dette betyr at barnekomponenten ikke kan legge seg selv i handlekurven ved å endre proppen inCart. Dette er ikke riktig.

Riktig måte er å be forelderkomponenten endre sin state prods, og legge det bestemte produktet i handlekurven.

La oss se på hvordan dette gjøres.

I forelderkomponenten lager vi en funksjon addToCart, som tar id til produktet som parameter og for dette produktet endrer egenskapen inCart til true:

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

I taggen med produktet legger vi til et attributt, der vi sender funksjonen vi har laget, og også et attributt der vi sender id til produktet:

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

Som du ser, kan man i props til komponenter sende ikke bare data, men også funksjoner.

Den endelige koden for komponenten Products blir følgende:

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

Nå vil funksjonen addToCart være tilgjengelig i barneklassen. La oss kalle denne funksjonen ved klikk på knappen, og sende den id til produktet som parameter:

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

Resultatet blir at ved klikk på knappen i barnekomponenten vil forelderens funksjon bli kalt, som vil endre forelderens state. Endring av forelderens state vil utløse en re-rendering og tegne produktet vårt på nytt, og sende den den endrede proppen.

Задание 1

Ta komponenten User fra forrige leksjon. Gjør slik at en knapp for å banne brukeren vises.