Cambiar el estado del componente padre en un componente hijo en React
En la lección anterior, el estado con los datos se almacenaba en el componente padre, y los componentes hijos recibían estos datos a través de props.
Ahora supongamos que queremos modificar nuestros productos.
Hagamos, por ejemplo, un botón que agregue
nuestro producto al carrito. Para empezar,
agreguemos a nuestro array de productos
el campo inCart, que indica si el producto
está en el carrito o no:
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},
];
En el componente Products, en la etiqueta del producto
agreguemos otro atributo 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>;
}
Hagamos en el componente hijo Product
la salida de información sobre el carrito y un botón
para agregar al carrito:
function Product({ id, name, cost, inCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'en el carrito' : 'no en el carrito'}</span>
<button>al carrito</button>
</div>;
}
Implementemos la adición
Según las reglas de React, un componente no debe
cambiar sus props. Esto significa que
el componente hijo no puede agregarse a sí mismo
al carrito cambiando la prop inCart.
Esto no es correcto.
Lo correcto es pedir al componente padre
que cambie su estado prods, colocando
un producto determinado en el carrito.
Veamos cómo se hace.
En el componente padre, hagamos una función addToCart,
que acepta como parámetro el id del producto
y para ese producto cambia la propiedad inCart
a true:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
En la etiqueta del producto agreguemos un atributo, al cual
le pasaremos la función que creamos, así como
un atributo, al cual le pasaremos el id del producto:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
Como puedes ver, en las props de los componentes se pueden pasar no solo datos, sino también funciones.
El código final de la función Products quedará
así:
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>;
}
Ahora en la función hija tendremos disponible
la función addToCart. Llamemos a esta función
al hacer clic en el botón, pasándole como parámetro
el id del producto:
function Product({ id, name, cost, inCart, addToCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'en el carrito' : 'no en el carrito'}</span>
<button onClick={() => addToCart(id)}>al carrito</button>
</div>;
}
El resultado será que al hacer clic en el botón en el componente hijo se llamará a la función del padre, que cambiará el estado del padre. El cambio del estado del padre provocará un rerenderizado y redibujará nuestro producto, pasándole la prop modificada.
Tome el componente User de la lección
anterior. Haga que aparezca un botón
para banear al usuario.