useState에서 상태 변경 시 바로 재렌더링 일어나지 않는 버그
🐞 문제상황
리스트 정렬 변경 시 변경된 리스트가 바로 적용되는 것이 아니라 변경 탭을 한번 더 클릭하면(modalVisible
상태가 변경되면) 그때 리스트가 변경된다
💡해결과정
기존 코드
정렬방법 탭을 클릭하면 모달이 열리면서
modalVisible
라는 상태가 false/true로 변경된다. 그리고 열려있는 모달에서 정렬방법을 클릭하면sort
를 사용하여 pickList의 순서를 변경시키고 sortedList에 저장한다. 이전 리스트와 순서가 다를 경우에만 setPickList에 sortedList를 넣어 상태를 변경시키도록 했다.const [modalVisible, setModalVisible] = useState(false); // 모달보기 const [order, setOrder] = useState<TOrder>('time'); // 정렬방법 useEffect(() => { if (pickList) { let sortedList; if (order === 'time') { // 최신순 sortedList = pickList.sort( (a, b) => new Date(b.CREA_DT).getTime() - new Date(a.CREA_DT).getTime(), ); } else if (order === 'recomment') { // 추천수 순 sortedList = pickList.sort((a, b) => b.RECOM_CNT - a.RECOM_CNT); } else { // 댓글 순 sortedList = pickList.sort((a, b) => b.COMMENT_CNT - a.COMMENT_CNT); } // 상태가 이전과 다를 때만 업데이트 if (JSON.stringify(pickList) !== JSON.stringify(sortedList)) { setPickList([...sortedList]); } } }, [order, pickList]);
문제는 pickList에 sort를 사용하여 이미 pickList 상태를 변경시켰고, 상태가 이전과 다를 경우 set함수를 실행하는 것에 부합하지 않기 때문이다.
esLint에러로 의존성 배열에 pickList를 넣었더니 setPickList가 계속 일어나면서 무한 렌더링이 발생했다. 이 무한 렌더링을 막기 위해 pickList와 sortedList를 비교해서 다를 경우에만 setPickList를 수행하도록 했다. 하지만 저 조건문은 이미 변경된 pickList와 그 복사본인 sortedList를 비교하고 있었던 것… pickList는 변경되었지만 setPickList를 통해 변경된 것이 아니기 때문에 재렌더링이 일어나지 않았고, modalVisible를 한번 더 클릭하면서 상태변경이 일어날 때 리스트도 같이 재렌더링되었다.
- 수정방향
useState의 상태를 바로 변경해주는 sort를 원본 배열에 직접 사용하는 것은 좋지 않으므로, […data] 를 사용하여 깊은 복사를 해주고 sort를 해준다.
의존성배열에 pickList를 삭제하고, 기존 배열과 비교하는 조건문도 삭제한다.
useEffect(() => {
if (pickList) {
let sortedList;
if (order === 'time') {
sortedList = [...pickList].sort((a, b) => new Date(b.CREA_DT).getTime() - new Date(a.CREA_DT).getTime());
} else if (order === 'recomment') {
sortedList = [...pickList].sort((a, b) => b.RECOM_CNT - a.RECOM_CNT);
} else {
sortedList = [...pickList].sort((a, b) => b.COMMENT_CNT - a.COMMENT_CNT);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
setPickList(sortedList);
}
}, [order]);
🎈결과
sort는 원본 배열을 변경시키므로 원본배열을 복사한 뒤 사용한다.
useEffect에서 useState의 상태를 의존성 배열에 넣고, 상태변화함수를 사용하면 무한렌더링이 일어난다.
useState의 상태는 데이터가 변경될때가 아닌 참조값이 변경되어야 렌더링이 트리거된다.
개발을 하면서 어이 없을 때는 알고 있는 개념인데 실제 코딩할 때는 그 개념을 자연스럽게 적용하여 코딩하는 것이 잘 안된다는 것이다. 이번 개념은 경험이 부족하다고 하기에 조금 민망할 정도의 기본 영역인 것 같다. 작업을 하면서 성공했다고 넘기는 것이 아니라 문제에 대해 파고들고 체화시키기 위해 시간을 가지는 것을 미루지 않아야겠다.
Subscribe to my newsletter
Read articles from 화영 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by