codesquad-masters2024-team02 / issue-tracker

그룹 프로젝트
4 stars 3 forks source link

무한 렌더 이슈 #58

Closed minjeongHEO closed 3 months ago

minjeongHEO commented 3 months ago

🐞 버그 설명

무한 렌더 발생

Image Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.



🚨버그가 발생한 상황

  1. Given:

    • React Query(useQueries)를 사용하여 데이터를 병렬로 가져오고, useFiltersData 훅을 통해 filterResults 배열로 반환합니다.
    • filterResults 배열은 여러 쿼리 상태를 포함합니다.
      import { useFiltersData } from '../../hooks/useFiltersData';
      const filterResults = useFiltersData();
      const [labelsResult, membersResult, milestonesOpenResult, milestonesClosedResult] = filterResults;
  2. When:

    • useEffect 훅의 종속성 배열에 filterResults를 설정합니다.
      
      useEffect(() => {
      if (filterResults.some((result) => !result.data)) return;

    const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({ title: name, })); ... 생략

    setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] })); }, [filterResults]);

  3. Then:

    • filterResults 배열 자체가 매 렌더링마다 새로운 참조로 생성되기 때문에 useEffect 훅이 무한 루프에 빠집니다.



😮 예상 동작 결과

각 fetch요청이 완료 될 때만 리렌더링이 될 것이라고 예상했습니다.



🤗 해결

useEffect의 종속성 배열에 filterResults를 설정했을 때 무한 루프에 빠지는 이유는, filterResults 배열 자체가 매 렌더링마다 새로운 참조로 생성되기 때문입니다. React Query의 useQueries 훅은 쿼리 상태를 반환하는 배열을 반환하지만, 이 배열이 매번 새로운 참조로 생성되기 때문에 useEffect가 지속적으로 실행되었던 것 입니다.

useEffect(() => {
        if (filterResults.some((result) => !result.data)) return;

        const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));
        const milestoneClosedItems = milestonesClosedResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));

        setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] }));
    }, [filterResults]);

🔽

useEffect(() => {
        if (filterResults.some((result) => !result.data)) return;

        const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));
        const milestoneClosedItems = milestonesClosedResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));

        setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] }));
    }, [labelsResult.data, membersResult.data, milestonesOpenResult.data, milestonesClosedResult.data]);