Changing Parent State in Child Component in React
In the previous lesson, we stored the state with data in the parent component, and the child components received this data in the form of props.
Now let's say we want to change our products. Let's make, for example, a button that will put our product in the cart. First, let's add a field inCart
to our array of products, showing whether the product is in the cart or not:
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},
];
In the Products
component, we will add another attribute inCart
to the product tag:
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>;
}
Let's make a display of information about the cart and a button for adding to the cart in the child component Product
:
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>;
}
We implement the addition
According to React rules, a component must not change its props. This means that a child component cannot put itself in the cart by changing the inCart
prop. This is not correct.
The correct way would be to ask the parent component to change its state prods
by adding a specific product to the cart.
Let's see how it's done.
In the parent component, we will create a function addToCart
, which takes the id
product as a parameter and changes the inCart
property for this product to true
:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
In the tag with the product, we will add an attribute to which we will pass the function we created, as well as an attribute to which we will pass id
of the product:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
As you can see, you can pass not only data but also functions to component props.
The final code for the Products
class will be as follows:
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>;
}
Now in the child class we will have the function addToCart
available. Let's call this function by clicking on the button, passing it the id
product parameter:
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>;
}
It turns out that when you click on a button in a child, the parent function will be called, which will change the parent state. Changing the parent state will cause re-rendering and re-create our product, passing it the changed props.
Take the User
component from the previous tutorial. Make it have a button to ban the user.