Reputation: 17383
I'm using nextjs.
I have a variable: delivery_times
.
I created this with useState
and it's empty at first time.
then with useEffect
function I called my api and I updated it.So far, so good.
now I added a property called is_selected
when use click on each object. default value is false
when it changed to true
, my element is added a selected
class.
const [delivery_times,setDeliveryTimes] = useState([])
useEffect(() => {
getDeliveryTimes()
}, []);
const getDeliveryTimes = () =>{
api.get('cart/delivery-times').then((res: any) => {
setDeliveryTimes(res.delivery_times)
}).catch(err => {
console.log(err);
})
}
const setSelectedDeliveryTime = (item) => {
for(var i = 0 ; i < delivery_times.length ; ++i){
delivery_times[i].is_selected = false;
}
const objIndex = delivery_times.findIndex((obj => obj.date == item.date));
console.log(objIndex,item)
delivery_times[objIndex].is_selected = true;
setDeliveryTimes(delivery_times) //it doesn't work
}
<div className="delivery-times mt-5">
{delivery_times.map(item => {
return (<div onClick={()=>setSelectedDeliveryTime(item)} className={item.is_selected === true?"item selected":"item"}>
<p className="date">{item.time_range}</p>
</div>
)
})}
</div>
but selected
class doesn't added at all.
Upvotes: 0
Views: 1376
Reputation: 17858
The reason being, as stated in their doc,
React uses Object.is to compare the previous value of your old and new state. Therefore, when you mutated delivery_times
and assign it back to state, react thought they were identical and therefore it doesn't bother to re-compute your state's value.
E.g.
const arr = [];
Object.is(arr, arr); // True
Object.is(arr, [...arr]); // False
As the previous answer, in order for your state to update, you'll need to update the state with a new array - There are multiple ways to clone an array, and spread is one of the most favorite, fastest and laziest way out there.
const setSelectedDeliveryTime = (item) => {
// Create new array, copied from delivery_times;
const new_delivery_times = [...delivery_times];
for(var i = 0 ; i < new_delivery_times.length ; ++i){
new_delivery_times[i].is_selected = false;
}
const objIndex = new_delivery_times.findIndex((obj => obj.date == item.date));
new_delivery_times[objIndex].is_selected = true;
setDeliveryTimes(new_delivery_times); // <-- notice it assigns new array.
}
Upvotes: 1
Reputation: 1785
Use state needs to change in order to trigger re-render, setDeliveryTimes(delivery_times)
sets the same array, although, one of its elements value is changed. you can do this:
setDeliveryTimes([...delivery_times])
which should trigger the re-render
Upvotes: 3