프로젝트

[Teamnote] 하나의 스코프 안에서 setState 연속 사용

삐옹 2022. 7. 28. 04:44

문제

소속된 워크스페이스 목록을 조회하는 요청과 내가 받은 초대장 목록을 조회하는 요청(GET요청 x2)이

2개의 useEffect 안에서 실행되고 있다.

전역 state를 이용해 같은 slice의 다른 키 값으로 업데이트 요청을 보내는데

소속된 워크스페이스 목록의 값을 잘 불러왔다가,
또 어떤 때는 값이 들어왔다가 다시 초기값으로 돌아가는 상황.

 

Redux로 저장한 배열의 값이 여러번의 렌더링을 거친다.

초깃값 -> 업데이트 -> 초깃값 or 초깃값 -> 업데이트 패턴으로 리렌더링 때마다 들어오는 값이 달라짐

그래서 어떤 때는 배열 값이 비어있어서 에러가 뜨고 또 어떤 때는 잘 읽어온다.

 

예) 첫 렌더링 - arr = Array(0) /  두 번째 렌더링 - arr = Array(3 ) / 세 번째 렌더링 arr = Array(0) => Error

예2) 첫 렌더링 - arr = Array(0) / arr = Array(3) => Success

원하는 결과

화면이 렌더링 된 뒤 소속된 워크스페이스 목록도 초대장 목록의 값도 잘 받아오기

접근

콘솔을 보니 분명 어딘가에서 state를 업데이트를 시킬 때 동시에 업데이트가 진행되면서 실행이 꼬여 무작위적으로 값이 업데이트 되는 것으로 예상했다.

아무리 뒤져봐도 dispatch 해주는 부분에서 업데이트를 실수로 초기화 시켰다거나 그런 부분은 안 보이는데..

 

문제의 코드

  // 소속된 워크스페이스 전체 목록 조회
  useEffect(() => {
    try {
      axios
        .get("http://43.200.170.45/api/members/spaceLists", {
          headers: {
            Authorization: `Bearer ${getItemFromLs("myToken")}`,
          },
        })
        .then((res) => {
          if (res.data.success) {
            const wsInfoList = res.data.includedList;
            const workSpaceFullname = wsInfoList.map((ws) => {
              return ws.workSpace;
            });
            if (wsInfoList.length === 0) {
              setAppState(APP_USER_STATE.NEWBIE);
              setOpenNewbieModal(true);
            } else {
              setAppState(APP_USER_STATE.USER);
            }
             dispatch(
               getUserInfo({
                ...user,
                 workSpaceList: [...workSpaceFullname], 
               }) // point(1)
             );
          }
        });
    } catch {
      alert(" 불러오는 도중 에러가 발생했습니다 :(");
    }
  }, []);

  // 본인에게 온 초대 여부 조회
  useEffect(() => {
    axios
      .get(`http://43.200.170.45/api/members/inviting`, {
        headers: {
          Authorization: `Bearer ${getItemFromLs("myToken")}`,
        },
      })
      .then((res) => {
        console.log("res: ", res);
        if (res.data.success) {
          dispatch(
            getUserInfo({
              ...user,
              invitation: [...res.data.result],
            }) // point(2)
          );
        }
      })
      .catch((err) => console.log(err));
  }, []);

불변성도 잘 지키면서 redux값을 업데이트 시켰는데 어디서 초기화를 시켜주는거지?

아무리 봐도 주석으로 처리해둔 point 부분말고는 의심할만한 곳이 없다.

 

그래서 두 axios를 합쳐서 dispatch를 하나로 묶어 업데이트 해주었더니

해결되었다. 

 

해결한 코드

  // 소속된 워크스페이스 전체 조회
  useEffect(() => {
    try {
      axios
        .get("http://43.200.170.45/api/members/spaceLists", {
          headers: {
            Authorization: `Bearer ${getItemFromLs("myToken")}`,
          },
        })
        .then((res) => {
          console.log("res: ", res);
          if (res.data.success) {
            const wsInfoList = res.data.includedList;
            console.log("wsInfoList: ", wsInfoList);
            const workSpaceFullname = wsInfoList.map((ws) => {
              return ws.workSpace;
            });
            if (wsInfoList.length === 0) {
              setAppState(APP_USER_STATE.NEWBIE);
              setOpenNewbieModal(true);
            } else {
              setAppState(APP_USER_STATE.USER);
            }

            axios
              .get(`http://43.200.170.45/api/members/inviting`, {
                headers: {
                  Authorization: `Bearer ${getItemFromLs("myToken")}`,
                },
              })
              .then((res) => {
                console.log("res: ", res);
                if (res.data.success) {
                  dispatch(
                    getUserInfo({
                      workSpaceList: [...workSpaceFullname],
                      invitation: [...res.data.result],
                    })
                  );
                }
              })
              .catch((err) => console.log(err));
          }
        });
    } catch {
      alert(" 불러오는 도중 에러가 발생했습니다 :(");
    }
  }, []);

 

한번 정리해보자.

컴포넌트가 렌더링 되면서 useEffect 2개가 동시에 실행이 된다.

게다가 그 안에서는 dispatch를 이용해 같은 slice의 다른 key값을 가진 state를 업데이트 시키고있다.

useEffect를 2번 호출했을 때 어떤 문제가 생기는 것인지 궁금해서 검색을 해보니

원래 React Hooks가 나오기 전에는 기본적으로

componentDidMount()나 componentDidUpdate()를 이용하여 side effect를 처리했는데

좀더 오류없이 유지보수도 쉽게 하기 좋도록 useEffect() 함수로 작성할 수 있게 되었다고 한다.

 

첫 렌더링 때 side effect를 중복해서 일으키므로 비동기 업데이트의 순서가 꼬여 업데이트 해야할 값들 다시 초깃값으로 돌려보낸 것 같다.

=> 질문방에 질문해보기

 


🙇🏻‍♂️참고한 블로그

https://points.tistory.com/86

 

[React] useEffect()와 Side-Effect

useEffect()? React에서 제공하는 useEffect()는 Side-Effect를 처리하기 위해 사용한다고 합니다. 그렇다면 Side-Effect는 무엇일까요? 의료계에서 말하는 부작용의 의미는 아니고, 컴퓨터 공학에서 사용하는

points.tistory.com

https://www.codeit.kr/community/threads/37594

 

코딩이 처음이라면, 코드잇

월 2만원대로 Python, JavaScript, HTML/CSS 등 2,600개 이상 프로그래밍 강의를 무제한 수강하세요

www.codeit.kr