2022-08-16 :: React swallow comparison, strict-mode

박세영·2022년 8월 16일
0


이번 포스팅은 오늘 하루 여닫을 수 있는 소식 목록을 구현하며 겪은 문제들을 정리하기 위해 작성한다.

객체 배열에서 객체의 변경사항을 감지하지 못하는 문제

// 기존 코드
const toggleNthIsOpen = (i: number) => {
    setNewsData(prev => {
        prev[i].isOpen = !prev[i].isOpen;
        return [...prev];
    });
};

기존 코드에서 객체 배열의 n번째 원소에 더 빠르게 접근하기 위해 인덱스로 타겟 객체를 직접 조작했다. useState의 세터함수에서 이전 상태를 인자로 받을 때 직접 조작해도 side effect가 없기 때문에 위와 같이 구현했다.

하지만, React는 상태 변화를 감지할 때 얕은 비교(swallow comparison)를 하기 때문에 배열 내부의 객체 내부의 프로퍼티의 변화까지는 감지하지 못하는 문제가 있었다. 이를 위해 배열과 타겟 객체를 깊은 복사를 통해 변경 해주어야만 얕은 비교로도 상태의 변화를 감지할 수 있다. 변경된 코드는 아래와 같다.

// 해결
const toggleNthIsOpen = (i: number) => {
    setNewsData(prev => {
        prev[i] = { ...prev[i], isOpen: !prev[i].isOpen };
        return [...prev];
    });
};

외부 패키지를 이용하는 방법도 있다. (use-deep-compare)
https://bestofreactjs.com/repo/sandiiarov-use-deep-compare-react-awesome-react-hooks

strict-mode에서 함수가 두 번 호출되는 문제

Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer

리액트는 strict-mode에서 아래 목록에 해당하는 함수들을 두 번씩 호출한다.

  • 클래스 컴포넌트의 constructor, render, shouldComponentUpdate 메서드
  • 클래스 컴포넌트 정적 getDerivedStateFromProps 메서드
  • 함수형 컴포넌트의 본문에 있는 함수들
  • 상태 업데이트 함수들
  • useState, useMemo, useReducer 함수들

일단은 명식적으로 react 프로젝트의 use-strict모드를 꺼서 에러를 해결했지만, 리엑트의 철학이 무엇인지. 어떡하면 side-effect가 없는 순수함수 형태로 작성할 수 있을지 고민해야겠다.

0개의 댓글