Ændring af forældrekomponentens state i et underkomponent i React

I den forrige lektion havde vi en state med data gemt i forældrekomponenten, mens underkomponenterne modtog disse data som props.

Lad os nu sige, at vi ønsker at ændre vores produkter. Lad os for eksempel lave en knap, som vil placere vores produkt i indkøbskurven. Til at starte med, lad os tilføje et felt inCart til vores array med produkter, som viser om produktet er i indkøbskurven eller ej:

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 tilføjer vi endnu en attribut inCart i tagget 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>;
}

Lad os i underkomponentet Product lave en udskrift af information om indkøbskurven og en knap til at tilføje til indkøbskurven:

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

Implementer tilføjelsen

Ifølge Reacts regler må en komponent ikke ændre sine egne props. Det betyder, at underkomponentet ikke selv kan lægge sig selv i indkøbskurven ved at ændre prop'en inCart. Det er ikke korrekt.

Korrekt vil være at bede forældrekomponenten om at ændre sin state prods, ved at lægge et bestemt produkt i indkøbskurven.

Lad os se på, hvordan det gøres.

I forældrekomponenten laver vi en funktion addToCart, som tager et id for produktet som parameter og for dette produkt ændrer egenskaben inCart til true:

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

I tagget med produktet tilføjer vi en attribut, hvori vi sender den funktion, vi har oprettet, samt en attribut, hvori vi sender produktets 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}
  />;
});

Som du kan se, kan man i komponenters props sende ikke kun data, men også funktioner.

Den endelige kode for klassen Products vil blive 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>;
}

Nu vil funktionen addToCart være tilgængelig i underklassen. Lad os kalde denne funktion ved et klik på knappen, ved at sende den id for 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 bliver, at ved et klik på knappen i underkomponentet kaldes forælderens funktion, som vil ændre forælderens state. Ændring af forælderens state vil udløse en genrendering og gen tegne vores produkt, ved at sende den den ændrede prop.

Задание 1

Tag komponentet User fra den forrige lektion. Lav det sådan, at der vises en knap for at banlyse en bruger.