반응형

배경

김뉴비는 사내 관리자페이지를 React, Redux-saga로 만들기로 했다. 천천히 학습시간을 갖고 개발하면 좋았겠지만, 시간이 부족해 개발부터하고 버그를 수정하며 React를 알아가는 중이다.

원하던 기능

DB에서 type별로 최신 컨텐츠 목록을 조회해서 store에 담는 것

(아래 코드는 예제를 위해 변형한 것입니다.)

const ContentListContainer = () => {
    const type = 'help';

    const dispatch = useDispatch();
    const {list, error, loading} = useSelector((contentList) => ({
        list: contentList.list,
        error: contentList.error,
    }));

    useEffect(() => {
        dispatch(getContentList(type));
    }, [dispatch]);

    return ...
};
import client from './client';

export const getContentList = (type) => {
    if (!type)
        throw new Error("getContentList type is falsy");

    return client.get(`/${type}`);
};

코드 설명

  1. useSelector를 통해 store에서 관리 중인 contentList 객체를 찾는다. (최초에는 초기화된 상태임)
  2. -- store -- contentList : { list : null, error : null }
  3. useEffect에서 getContentList 액션 함수를 호출한다. 조회된 DB 데이터 contentList 객체에 update 한다.
  4. -- store -- contentList : { list : {...}, error : null }

에러 발생

Error: Maximum update depth exceeded 발생

console과 서버 로그를 보니 GET API가 미친듯이 날라가고 있음

원인

useEffect()는 랜더링이 초기화된 후 실행된다. 이때 line 10에서 getContentList() 라는 Action을 dispatch(실행)시킨다.

getContentList() 는 DB의 데이터를 조회해서 store에 있는 contentList 를 Update하는데, 이 때 트리거가 발생하면서 컴포넌트가 리랜더링된다 (ㅎㅎㅎ)

컴포넌트가 리랜더링 되면서 다시 useEffect() 를 호출하고 getContentList() Action이 실행된다.

React%20useEffect%20%E1%84%82%E1%85%A2%20%E1%84%86%E1%85%AE%E1%84%92%E1%85%A1%E1%86%AB%E1%84%85%E1%85%AE%E1%84%91%E1%85%B3%20%E1%84%8B%E1%85%AF%E1%86%AB%E1%84%8B%E1%85%B5%E1%86%AB%E1%84%80%E1%85%AA%20%E1%84%92%E1%85%A2%E1%84%80%E1%85%A7%E1%86%AF%20cb04073206b4464081d0e0b5edc990f9/1Cqd-x6wVKHFpd1zk5sGayg.png

예제 코드와 이미지의 명이 일치하지 않는다. 정확히 알고 싶다면 아래 원문글을 참고하길 바란다.

원문 : medium.com/@andrewmyint/infinite-loop-inside-useeffect-react-hooks-6748de62871

해결

useEffect() 가 초기화 시점과 store의 state가 변경되었을 때에만 실행되도록 변경한다.

useEffect() 의 두번째 파라미터에 type 을 넘겨서, 최초 랜더링때와 type 값이 변경되었을 때에만 컴포넌트를 리랜더링 하도록 한다.

const ContentListContainer = () => {
    const type = 'help';

    const dispatch = useDispatch();
    const {list, error, loading} = useSelector((contentList) => ({
        list: contentList.list,
        error: contentList.error,
    }));

    useEffect(() => {
        dispatch(getContentList(type));
    }, [dispatch, type]);

    return ...
};

느낀 점

아... 웬만하면 프레임워크에 대해 모르는 상태로 개발을 해도 성능 이슈가 크게 발생하지 않는데, 리액트는 좀 공부하고 개발을 하는 게 좋겠다는 생각이 든다.

반응형