늘 아무 생각없이 썼었던 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 함수가 사용하고 있는 외부의 값들의 의존성이다.
위 코드를 보면 외부에 있는 fetchFollowing
과 getUserData
를 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]); // 넣어주기
그래서 의존성 배열안에 fetchFollowing
과 getUserData
를 넣어주었다. 그랬더니 일어난 일은 무한 렌더링
이었다.
왜그런지 알아보니 fetchFollowing
과 getUserData
는 비동기 함수로 데이터를 반환하고 있는 함수이다. useEffect가 실행될 때마다 매번 새로운 함수가 생성되어 무한렌더링이 발생하는 것이라고 한다.
해당 함수가 매번 새롭게 생성되지 않게 해주는 메모이제이션 방법을 사용한다.
먼저 fetchFollowing
과 getUserData
코드를 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이 바뀔 때만 함수를 새롭게 생성되게 한다. 그리고 사용하는 코드는 동일하게 작성한다.
드디어!! 무한렌더링에서 벗어날 수 있었다. 하지만,
메모이제이션은 자주 사용하면 안되고 마지막 수단으로 남겨두어야한다
그렇기 때문에 처음부터 고려하고 커스텀훅을 더 커스텀 훅답고 재사용가능하게 만들었어야 했다고 생각한다. 천천히 공부하면서 더 리팩토링을 해보기로..!