Modification de l'état du parent dans un composant enfant dans React
Dans la leçon précédente, notre état avec les données était stocké dans le composant parent, et les composants enfants recevaient ces données sous forme de props.
Supposons maintenant que nous voulons modifier nos produits.
Créons, par exemple, un bouton qui placera
notre produit dans le panier. Pour commencer,
ajoutons à notre tableau de produits
un champ inCart, indiquant si le produit
est dans le panier ou non :
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},
];
Dans le composant Products, dans la balise du produit,
ajoutons un autre attribut 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>;
}
Dans le composant enfant Product,
ajoutons l'affichage de l'information sur le panier et un bouton
pour ajouter au panier :
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>;
}
Implémentons l'ajout
Selon les règles de React, un composant ne doit pas
modifier ses propres props. Cela signifie que
le composant enfant ne peut pas se mettre lui-même
dans le panier en modifiant la prop inCart.
Ce n'est pas correct.
La bonne méthode est de demander au composant parent
de modifier son état prods, en mettant
un certain produit dans le panier.
Voyons comment cela se fait.
Dans le composant parent, créons une fonction addToCart,
qui accepte en paramètre l'id du produit
et pour ce produit change la propriété inCart
en true :
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
Dans la balise du produit, ajoutons un attribut, dans lequel
nous passerons la fonction que nous avons créée, ainsi que
un attribut, dans lequel nous passerons l'id du produit :
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
Comme vous le voyez, dans les props des composants, on peut passer non seulement des données, mais aussi des fonctions.
Le code final de la classe Products sera
le suivant :
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>;
}
Maintenant, dans la classe enfant, nous aurons accès à
la fonction addToCart. Appelons cette fonction
au clic sur le bouton, en lui passant en paramètre
l'id du produit :
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 en résultera que lors du clic sur le bouton dans l'enfant, la fonction du parent sera appelée, ce qui changera l'état du parent. La modification de l'état du parent déclenchera un re-rendu et redessinera notre produit, en lui passant la prop modifiée.
Prenez le composant User de la leçon
précédente. Faites en sorte qu'un bouton
pour bannir l'utilisateur y apparaisse.