Search code examples
reactjslocal-storageinfinite-loopuse-effect

ReactJS - use localStorage as a dependency for useEffect causes infinite loop


This code give me infinite loop at line console.log

const userInfo = JSON.parse(localStorage.getItem("user_info"));
const [filterSemester, setFilterSemester] = useState(SEMESTERS[0]);
const [scoreData, setScoreData] = useState(null);

useEffect(() => {
  getData();
}, [userInfo, filterSemester]);

useEffect(() => {
  console.log("scoreData: ", scoreData);
}, [scoreData]);

const getData = () => {
  const params = {
    student_id: userInfo?.student_info?.id,
    school_year_id:
      userInfo?.student_info?.class_info?.grade_info?.school_year_id,
    semester: filterSemester.key,
  };
  getStudyInfoBySchoolYear(params).then((res) => {
    if (res?.status === 200) {
      setScoreData(res?.data?.data);
    }
  });
};

If I remove userInfo from the dependency array of the first useEffect, the loop will gone, I wonder why? I didn't change it at all in the code.


Solution

  • userInfo is actually changing.

    It is a functional component, so all the code that is inside the component will run on every render, thus, userInfo gets re-created on every render, because it was not declared as a reference (with useRef) or, more commonly, as a state (with useState).

    The flow is as follows:

    1. The component mounts.
    2. The first useEffect runs getData. The second useEffect also runs.
    3. getData will update scoreData state with setScoreData. This latter will trigger a re-render, and also scoreData has changed, so the second useEffect will run.
    4. When the render takes place, all the code within your component will run, including the userInfo declaration (creating a new reference to it, unless localStorage.getItem("user_info") is returning undefined).
    5. React detects userInfo as changed, so the first useEffect will run again.
    6. The process repeats from step 3.

    You could replace your

    const userInfo = JSON.parse(localStorage.getItem("user_info"));
    

    with

      const userInfo = React.useRef(JSON.parse(localStorage.getItem("user_info")));
    

    and your

    useEffect(() => {
      getData();
    }, [userInfo, filterSemester]);
    

    with

      useEffect(() => {
        getData();
      }, [userInfo.current, filterSemester]);