Zmiana stanu rodzica w komponencie potomnym w React
W poprzedniej lekcji stan z danymi był przechowywany w komponencie nadrzędnym, a komponenty potomne otrzymywały te dane w postaci propsów.
Załóżmy teraz, że chcemy modyfikować nasze produkty.
Zróbmy na przykład przycisk, który będzie
umieszczał nasz produkt w koszyku. Na początek
dodajmy do naszej tablicy z produktami
pole inCart, pokazujące, czy produkt jest
w koszyku, czy nie:
const 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},
];
W komponencie Products do tagu z produktem
dodajmy jeszcze jeden atrybut inCart:
function 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>;
}
Zróbmy w komponencie potomnym Product
wypisanie informacji o koszyku i przycisk
do dodania do koszyka:
function 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>;
}
Zaimplementujmy dodawanie
Zgodnie z zasadami React komponent nie powinien
modyfikować swoich propsów. Oznacza to, że
komponent potomny nie może sam siebie
umieścić w koszyku, zmieniając prop inCart.
To nie jest poprawne.
Poprawnie będzie poprosić komponent nadrzędny
o zmianę jego stanu prods, umieszczając
określony produkt w koszyku.
Przyjrzyjmy się, jak to się robi.
W komponencie-rodzicu stwórzmy funkcję addToCart,
która przyjmuje parametr id produktu
i dla tego produktu zmienia właściwość inCart
na true:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
Do tagu z produktem dodajmy atrybut, do którego
przekażemy stworzoną przez nas funkcję, a także
atrybut, do którego przekażemy id produktu:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
Jak widać, do propsów komponentów można przekazywać nie tylko jakieś dane, ale i funkcje.
Ostateczny kod komponentu Products będzie
następujący:
function 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>;
}
Teraz w komponencie potomnym będziemy mieli dostępną
funkcję addToCart. Wywołajmy tę funkcję
po kliknięciu na przycisk, przekazując jej parametrem
id produktu:
function 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>;
}
Okaże się, że po kliknięciu przycisku w komponencie potomnym zostanie wywołana funkcja rodzica, która zmieni stan nadrzędny. Zmiana stanu nadrzędnego spowoduje ponowne renderowanie i przerysowanie naszego produktu, przekazując mu zmieniony prop.
Weź komponent User z poprzedniej
lekcji. Zrób tak, aby pojawił się w nim
przycisk do zbanowania użytkownika.