Reputation: 18545
I want to achieve the effect when PRODUCTS
change (during button click), the list changes as well with first category change to "haha".
The whole objective is to let component rerender during props change. But seems like it doesn't work. Below is the code.
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
const FilterableProductTable = (props) =>{
return (
<ul>
{props.products.map((product) =>
(<li>{product.category}</li>)
)}
</ul>
);
}
const PRODUCTS = [
{ category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football' },
{ category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball' },
{ category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball' },
{ category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch' },
{ category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5' },
{ category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7' }
];
const Home = () => {
const [productData, changeProduct] = useState(PRODUCTS);
const handleClick = () => {
PRODUCTS[0].category = 'haha';
changeProduct(PRODUCTS);
console.log(PRODUCTS);
};
return (
<>
<button onClick={handleClick}>haha</button>
<FilterableProductTable products={productData} />
</>
);
};
export default Home;
You can try it here:
How to let FilterableProductTable
rerender during props change?
Upvotes: 0
Views: 51
Reputation: 1568
useState will trigger a re-render, only if its current value is different than the current state. It will do a Object.is
or ===
comparision. Here you are setting the same reference PRODUCTS
as in the defaultState and value passed to changeProduct
is also having the same reference as PRODUCTS
which will not trigger a re-render.
Quoting from React DOCS
If your update function returns the exact same value as the current state, the subsequent rerender will be skipped completely.
const handleClick = () => {
const res = productData.map((prod,index) => index === 0 ? {...prod,category:"haha"} : prod)
changeProduct(res);
console.log(res);
};
map
will create a new array every time.
Upvotes: 1
Reputation: 684
when changing a property in array, react doesnt see it as a change, you can use spread operator to clone your array and set new array:
changeProduct([...PRODUCTS])
Upvotes: 0
Reputation: 84982
When you set state in a function component, react does a ===
between the old and new state. If they're the same, then react skips rendering. Since you are mutating state, it's the same array before and after, and the render is skipped.
The fix is to keep your state immutable. Don't modify the old array, but instead create a new one:
const handleClick = () => {
changeProducts(prev => {
const next = [...prev];
next[0] = {
...next[0],
category: 'haha';
}
return next;
});
}
Upvotes: 1