Modificare lo stato del genitore in un componente figlio in React
Nella lezione precedente, lo stato con i dati era memorizzato nel componente genitore, mentre i componenti figli ricevevano questi dati sotto forma di props.
Ora supponiamo di voler modificare i nostri prodotti.
Creiamo, ad esempio, un pulsante che
aggiunga il nostro prodotto al carrello. Per iniziare
aggiungiamo al nostro array di prodotti
un campo inCart, che indica se il prodotto
è nel carrello o meno:
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},
];
Nel componente Products, aggiungiamo un altro attributo
inCart al tag del prodotto:
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>;
}
Creiamo nel componente figlio Product
un output per l'informazione sul carrello e un pulsante
per l'aggiunta al carrello:
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>;
}
Implementiamo l'aggiunta
Secondo le regole di React, un componente non dovrebbe
modificare le sue props. Ciò significa che
un componente figlio non può aggiungere sé stesso
al carrello modificando la prop inCart.
Non è corretto.
Il modo corretto è chiedere al componente genitore
di modificare il suo stato prods, impostando
un determinato prodotto come nel carrello.
Vediamo come si fa.
Nel componente genitore, creiamo una funzione addToCart,
che accetta come parametro l'id del prodotto
e per quel prodotto cambia la proprietà inCart
in true:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
Aggiungiamo al tag del prodotto un attributo, in cui
passiamo la funzione da noi creata, e anche
un attributo, in cui passiamo l'id del prodotto:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
Come puoi vedere, nei props dei componenti è possibile passare non solo alcuni dati, ma anche funzioni.
Il codice finale del componente Products risulterà
il seguente:
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>;
}
Ora nella classe figlio avremo a disposizione
la funzione addToCart. Chiamiamo questa funzione
al click del pulsante, passandole come parametro
l'id del prodotto:
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>;
}
Il risultato sarà che al click sul pulsante nel figlio verrà chiamata la funzione del genitore, che cambierà lo stato del genitore. La modifica dello stato del genitore causerà un re-rendering e ridisegnerà il nostro prodotto, passandogli la prop modificata.
Prendi il componente User dalla lezione precedente.
Fai in modo che appaia un pulsante per bannare l'utente.