skip to Main Content

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


  1. 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:

    useEffect(() => {
      //Runs on every render
    });
    

    2- if you want it to be executed just at the first render, same as componentDidMount, use it like this:

    useEffect(() => {
      //Runs only on the first render
    }, []);
    

    3- if you want it to be executed depending on the change of a value or multiple values, use it like this:

    useEffect(() => {
      //Runs on the first render
      //And any time any dependency value changes
    }, [value1, value2]);
    
    Login or Signup to reply.
  2. rerender means executing the body of the component again and again. Since you defined them just as regular variables

    const normalContactGroup: { [key: string]: Contact[] } = {};
    const favoriteContactGroup: { [key: string]: Contact[] } = {};
    

    Their 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search