React에서 Array.sort()는 렌더링 되지 않는다 (feat. Shallow Copy)

seohyun Kang·2023년 5월 11일
0

React

목록 보기
7/9

이슈

테이블의 ASC, DESC Sorting 기능을 테스트하던 중에 아래와 같은 이슈를 확인했습니다.

const desc = useCallback(() => {
	setState(prev => prev.sort((a, b) => b - a));
},[]);

> Not re-rendered

const desc = useCallback(() => {
	const newState = state.sort((a, b) => b - a));
	setState(newState);
},[state]);

> Not re-rendered

const desc = useCallback(() => {
	setItems((prev) => {
      const newArray = prev.sort((a, b) => b.id - a.id);
      return newArray;
    });
},[])

> Not re-rendered

const desc = useCallback(() => {
	setState((prev) => {
    	const newArray = prev.sort((a, b) => b - a);
        return [...newArray];
    });
},[state]);

> re-rendered

위와 같은 결과가 생기는 이유에 대해서 찾아보았습니다.

해당 이슈를 이해하기 위해서는 원시값(primitive)과 참조값(reference)부터 이해해야합니다.

원시값(Primitive) & 참조값(reference)

원시값과 참조값은 자바스크립트에서 변수를 저장하는 두 가지 타입니다.

원시값(Primitive)

자바스크립트의 원시(primitive)값은 변경할 수 없는 값(원시값을 변경하려면 새로운 값을 할당해야한다)으로, 다른 값들과는 다르게 메모리 상에서 고정된 크기를 가지고 있습니다. 자바스크립트의 원시값은 다음과 같습니다.

  1. 숫자(number): 정수와 부동소수점을 모두 포함합니다.
  2. 문자열(string): 따옴표(작은 따옴표 또는 큰 따옴표)로 묶인 문자들의 시퀀스입니다.
  3. 불리언(boolean): true 또는 false 값을 가집니다.
  4. null: 값이 없음을 나타냅니다.
  5. undefined: 정의되지 않은 값을 나타냅니다.
  6. 심볼(symbol): ES6에서 추가된 원시값으로, 유일한 값입니다.

원시값은 변수에 할당될 때 복사됩니다. 이는 변수 간에 값이 서로 영향을 주지 않도록 보장합니다. 또한 원시값은 메모리를 효율적으로 사용하며, 값의 변경이 불가능하기 때문에 예기치 않은 결과를 방지할 수 있습니다.

참조값(Reference)

자바스크립트의 참조값(reference value)은 객체(object)와 함수(function)입니다. 참조값은 변수에 직접적으로 값이 저장되는 것이 아니라, 값이 저장된 메모리 주소에 대한 참조(reference)를 저장합니다. 이 참조를 통해 객체나 함수의 내용에 접근하고 변경할 수 있습니다.

여기서 우리는 참조값의 메모리 주소에 대한 참조를 저장이라는 문구를 주시해야합니다.

Re-render가 되지 않는 이유

React에서 array.sort() 메소드를 호출하여 정렬된 새로운 배열을 setState를 통해 상태(state)로 설정하면, 리렌더링이 되지 않는 이유는 불변성(immutability) 원칙과 관련이 있습니다.

React는 상태(state)가 변경되었을 때, 이전 상태와 새로운 상태를 비교하여 변경된 부분만 업데이트하고, 나머지 부분은 그대로 둬서 최적화를 수행합니다. 이를 위해서는 이전 상태와 새로운 상태가 서로 다른 객체여야 합니다. 즉, 상태의 변경은 항상 새로운 객체를 생성하여 변경된 상태를 저장해야 합니다.

하지만 array.sort() 메소드는 정렬된 새로운 배열을 생성하는 것이 아니라, 원본 배열이 변경되어 같은 객체가 유지(배열의 메모리 주소는 유지되고 내용만 변경)됩니다. 이 경우, 상태의 변경이 감지되지 않기 때문에 리렌더링이 되지 않습니다.

결론적으로,

결론적으로, React에서는 컴포넌트의 상태(state)나 속성(props)이 변경되었는지를 객체의 얕은 비교(shallow comparison)를 통해 확인합니다. 객체의 얕은 비교는 객체의 참조 주소가 동일한지 비교하기 때문에 정렬된 새로운 배열을 새로운 객체 참조 주소에 할당해야합니다.

sort와 같이 원본 배열을 변경하는 메소드는 pop, push, shift 등이 있습니다.

해결방안

원본 배열에 slice(), spread operator(...)를 통해 객체 참조 주소를 재할당하는 방법이 있습니다.

0개의 댓글