useEffect 의존성배열 ESLint warning, React Hook useEffect has missing dependencies

Doha Lee·2023년 9월 1일
0

React

목록 보기
6/7
post-thumbnail

useEffect 의존성배열

늘 아무 생각없이 썼었던 useEffect를 ESLint에게 크게 혼나면서 의존성 배열에 대해서 다시 생각해보게 되었다.
의존성 배열이란? useEffect에 두번째로 인자로 넘기는 배열이다. 두번째 인자가 없으면 effect는 매번 실행되고, 빈 배열이라면 렌더링시 최초 한 번만 실행 된다. 까지는 알고 있었던 이야기였지만, 왜 빈 배열 일때 missing dependencies 에러가 뜨는지는 정확하게 알지 못했다.

에러 상황

const { fetchFollowing } = FollowingListAPI(accountname);
const { getUserData } = MyInfoAPI();

useEffect(() => {
  const getData = async () => {
    const followingData = await fetchFollowing();
    const userData = await getUserData();
    followingData && setMyFollowing(followingData.slice(0, 5));
    userData && setUser(userData);
  };

  getData();
}, []);

위 코드에서 빈배열에 missing dependencies 에러가 발생했다.

React Hook useEffect has missing dependencies: 'fetchFollowing' and 'getUserData'. Either include them or remove the dependency array react-hooks/exhaustive-deps

useEffect에서 의존성 배열이란 "effect 함수가 의존하고 있는 요소들의 모음"이다. 즉, effect 함수가 사용하고 있는 외부의 값들의 의존성이다.

위 코드를 보면 외부에 있는 fetchFollowinggetUserData를 useEffect 안에서 사용하고 있다. 그렇기 때문에 ESLint 경고가 뜨는 것이다.

const { fetchFollowing } = FollowingListAPI(accountname);
const { getUserData } = MyInfoAPI();

useEffect(() => {
  const getData = async () => {
    const followingData = await fetchFollowing();
    const userData = await getUserData();
    followingData && setMyFollowing(followingData.slice(0, 5));
    userData && setUser(userData);
  };

  getData();
}, [fetchFollowing, getUserData]); // 넣어주기

그래서 의존성 배열안에 fetchFollowinggetUserData를 넣어주었다. 그랬더니 일어난 일은 무한 렌더링이었다.
왜그런지 알아보니 fetchFollowinggetUserData는 비동기 함수로 데이터를 반환하고 있는 함수이다. useEffect가 실행될 때마다 매번 새로운 함수가 생성되어 무한렌더링이 발생하는 것이라고 한다.

해결방법

해당 함수가 매번 새롭게 생성되지 않게 해주는 메모이제이션 방법을 사용한다.

먼저 fetchFollowinggetUserData 코드를 useCallback을 사용하여 메모이제이션 한다.

const FollowingListAPI = (accountname) => {
  const token = useRecoilValue(userToken);

  // useCallback 사용
  const fetchFollowing = useCallback(async () => {
    try {
      const response = await fetch(`${URL}/profile/${accountname}/following`, {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
      });

      const data = await response.json();
      return data;
    } catch (error) {
      console.error('API 응답에 실패하였습니다.', error);
    }
  }, [accountname, token]); // accountname, token 의존

  return { fetchFollowing };
};

export default FollowingListAPI;
const { fetchFollowing } = FollowingListAPI(accountname);
const { getUserData } = MyInfoAPI();
const [myFollowing, setMyFollowing] = useState([]);
const [user, setUser] = useState();

useEffect(() => {
  const getData = async () => {
  const followingData = await fetchFollowing();
  const userData = await getUserData();
  followingData && setMyFollowing(followingData.slice(0, 5));
  userData && setUser(userData);
  };

  getData();
}, [fetchFollowing, getUserData]);

useCallback 의존성 배열에는 바뀔 수 있는 accountname과 token을 넣어서 accountname과 token이 바뀔 때만 함수를 새롭게 생성되게 한다. 그리고 사용하는 코드는 동일하게 작성한다.

드디어!! 무한렌더링에서 벗어날 수 있었다. 하지만,

메모이제이션은 자주 사용하면 안되고 마지막 수단으로 남겨두어야한다

그렇기 때문에 처음부터 고려하고 커스텀훅을 더 커스텀 훅답고 재사용가능하게 만들었어야 했다고 생각한다. 천천히 공부하면서 더 리팩토링을 해보기로..!

0개의 댓글

관련 채용 정보