i’m trying to use this use effect in my project but it makes my component fails to render correctly:
useEffect(() => {
setFavoriteContact((item) => {
const favorites = window?.localStorage?.getItem("favorites")
? JSON.parse(`${localStorage.getItem("favorites")}`)
: [];
const favoritas = [...item, ...favorites];
contacts.forEach((item) => {
const firstLetter = item.last_name.charAt(0).toUpperCase();
if (favoritas.includes(`${item.last_name} ${item.first_name}`)) {
if (!favoriteContactGroup[firstLetter]) {
favoriteContactGroup[firstLetter] = [];
}
favoriteContactGroup[firstLetter].push(item);
} else {
if (!normalContactGroup[firstLetter]) {
normalContactGroup[firstLetter] = [];
}
normalContactGroup[firstLetter].push(item);
}
});
return favoritas;
});
}, []);
after some tinkering i found out that if i modify the code like this, then now it renders correctly albeit having some awkward delay before showing the data (it doesn’t instantly put the favorite contact in the right place):
useEffect(() => {
const favorites = window?.localStorage?.getItem("favorites")
? JSON.parse(`${localStorage.getItem("favorites")}`)
: [];
setFavoriteContact((item) => [...item, ...favorites]);
}, []);
contacts.forEach((item) => {
const firstLetter = item.last_name.charAt(0).toUpperCase();
if (favoriteContact.includes(`${item.last_name} ${item.first_name}`)) {
if (!favoriteContactGroup[firstLetter]) {
favoriteContactGroup[firstLetter] = [];
}
favoriteContactGroup[firstLetter].push(item);
} else {
if (!normalContactGroup[firstLetter]) {
normalContactGroup[firstLetter] = [];
}
normalContactGroup[firstLetter].push(item);
}
});
i need some explanation as to why the first snippet fail to render my component but the second one actually works. i’ve been thinking that it maybe because the value didn’t get properly processed but when i console log them, it output the right values (i tried logging the normalContactGroup and favoriteContactGroup). so maybe it’s because for some reason react does not re-render my component afterwards? but why?
===
Edit 1:
favoriteContactGroup and normalContactGroup are an empty object for storing the grouped contact data from my backend, they look like this initially:
const normalContactGroup: { [key: string]: Contact[] } = {};
const favoriteContactGroup: { [key: string]: Contact[] } = {};
and the data from the backend has this shape:
const contactArray: Contact[] = [
{
_id: "6561a98995095b8c3c8b38ca",
contact_id: "6561a98995095b8c3c8b38c6",
first_name: "Poppy",
last_name: "Abba",
default_number: "980891",
phone_numbers: 2,
createdAt: "2023-11-25T08:00:09.442Z",
updatedAt: "2023-11-25T08:00:09.442Z",
__v: 0,
},
{
_id: "6561a97695095b8c3c8b38c4",
contact_id: "6561a97695095b8c3c8b38c0",
first_name: "Berkley",
last_name: "Arkham",
default_number: "89701234",
phone_numbers: 2,
createdAt: "2023-11-25T07:59:50.618Z",
updatedAt: "2023-11-25T07:59:50.618Z",
__v: 0,
},
];
but after that processing, they turn to be like this:
{
"A": [
{
"_id": "6561a98995095b8c3c8b38ca",
"contact_id": "6561a98995095b8c3c8b38c6",
"first_name": "Poppy",
"last_name": "Abba",
"default_number": "980891",
"phone_numbers": 2,
"createdAt": "2023-11-25T08:00:09.442Z",
"updatedAt": "2023-11-25T08:00:09.442Z",
"__v": 0
},
{
"_id": "6561a97695095b8c3c8b38c4",
"contact_id": "6561a97695095b8c3c8b38c0",
"first_name": "Berkley",
"last_name": "Arkham",
"default_number": "89701234",
"phone_numbers": 2,
"createdAt": "2023-11-25T07:59:50.618Z",
"updatedAt": "2023-11-25T07:59:50.618Z",
"__v": 0
},
],
}
2
Answers
There three cases for
useEffect
1- if you want it to be executed in each render you should not specify the array of prop, like this:
2- if you want it to be executed just at the first render, same as
componentDidMount
, use it like this:3- if you want it to be executed depending on the change of a value or multiple values, use it like this:
rerender
means executing the body of the component again and again. Since you defined them just as regular variablesTheir values get reset to empty objects on each render.
In your first example you update their values, but since the rerender happens after that, their values got reset.
Second example updates their values after the rerender. So on every rerender they get reset and update again and again
You should not do side effects inside setState callback. It should only return the new state