[Teamnote] 하나의 스코프 안에서 setState 연속 사용
문제
소속된 워크스페이스 목록을 조회하는 요청과 내가 받은 초대장 목록을 조회하는 요청(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://www.codeit.kr/community/threads/37594