리액트의 가상 DOM과 실제 DOM의 비교, 리액트 컴포넌트가 렌더링 할지를 판단하는 방법, 변수나 함수의 메모이제이션 등 모든 작업은 자바스크립트의 동등 비교를 기반으로 한다.
자바스크립트에서 값을 비교하기 위한 세가지 방법이 있다.
==
연산자===
연산자[Object.is](http://Object.is)
메서드Object.is
메서드는 다음과 같은 차이점이 있다.
-0 === +0 // true
Object.is(-0, +0) // false
Number.NaN === NaN // false
Object.is(Number.NaN, NaN) // true
NaN === 0 / 0 // false
Object.is (NaN, 0 / 0) //true
위의 예시를 보면 알 수 있듯이 Object.is
가 좀 더 개발자가 기대하는 방식으로 정확히 비교한다. 그러나 여전히 객체 간 비교에 있어서는 ===
과 동일하게 동작한다.
// 참조가 다른 객체에 대해 비교 불가능
Object.is({ hello: 'world' }, { hello: 'world' }) // false
리액트에서는 Object.is
를 이용해 동등 비교를 한다. 구형 브라우저 지원을 위해 폴리필을 함께 사용한다.
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
const objectIs: (x: any, y: any) => boolean =
// $FlowFixMe[method-unbinding]
typeof Object.is === 'function' ? Object.is : is;
export default objectIs;
https://github.com/facebook/react/blob/main/packages/shared/objectIs.js
리액트에서는 이 objectIs
함수를 기반으로 동등 비교를 하는 shallowEqual
이라는 함수를 만들어 사용한다. shallowEqual
은 의존성 비교 등 리액트의 동등 비교가 필요한 다양한 곳에서 사용된다.
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!hasOwnProperty.call(objB, currentKey) ||
// $FlowFixMe[incompatible-use] lost refinement of `objB`
!is(objA[currentKey], objB[currentKey])
) {
return false;
}
}
return true;
}
https://github.com/facebook/react/blob/main/packages/shared/shallowEqual.js
리액트에서의 비교를 요약하면 Object.is
로 먼저 비교를 수행한 다음, Object.is
로 수행하지 못하는 비교, 즉 객체 간 얕은 비교를 한번 더 수행한다.
기본적으로 리액트는 props
에서 꺼내온 값을 기준으로 렌더링을 수행하기 때문에 일반적인 케이스에서는 얕은 비교로 충분하다. 이러한 특성을 안다면 props
에 또 다른 객체를 넘겨준다면 리액트 렌더링이 예상치 못하게 작동하는 것을 알 수 있다.
얕은 비교로만 수행하는 이유는 깊은 비교를 할 경우 객체안에 얼마나 중첩이 되어있는지 모르기 때문에 성능에 악영향을 미칠 것이다. 이러한 언어적 한계를 뛰어넘을 수 없어 얕은 비교만을 사용해 비교한다.
참조: 모던 리액트 Deep Dive