반응에서 얕은 비교는 어떻게 작동합니까?


96

React 문서 에서는 다음 과 같이 말합니다.

shallowCompare는 현재 상태 및 nextState 객체뿐 아니라 현재 props 및 nextProps 객체에 대해 얕은 동등성 검사를 수행합니다.

내가 이해할 수없는 것은 객체를 얕게 비교하면 shouldComponentUpdate 메서드가 항상 true를 반환합니다.

상태를 변경해서는 안됩니다.

상태를 변경하지 않으면 비교는 항상 거짓을 반환하므로 shouldComponent 업데이트는 항상 참을 반환합니다. 나는 그것이 어떻게 작동하는지 그리고 성능을 향상시키기 위해 이것을 어떻게 재정의 할 것인지에 대해 혼란 스럽습니다.

답변:


132

얕은 비교는 동등성을 확인합니다. 스칼라 값 (숫자, 문자열)을 비교할 때 값을 비교합니다. 객체를 비교할 때 객체의 속성을 비교하지 않습니다. 참조 만 비교됩니다 (예 : "동일한 객체를 가리 킵니까?).

다음 user물체의 모양을 고려해 봅시다

user = {
  name: "John",
  surname: "Doe"
}

예 1 :

const user = this.state.user;
user.name = "Jane";

console.log(user === this.state.user); // true

사용자 이름을 변경했습니다. 이 변경으로도 개체는 동일합니다. 참조는 정확히 동일합니다.

예 2 :

const user = clone(this.state.user);
console.log(user === this.state.user); // false

이제 개체 속성을 변경하지 않고도 완전히 다릅니다. 원본 개체를 복제하여 다른 참조를 사용하여 새 복사본을 만듭니다.

복제 기능은 다음과 같을 수 있습니다 (ES6 구문).

const clone = obj => Object.assign({}, ...obj);

얕은 비교는 변경 사항을 감지하는 효율적인 방법입니다. 데이터를 변경하지 않을 것으로 예상합니다.


그래서 만약 우리가 코드를 작성한다면 스칼라 값이 있다면 그것들을 변경해야합니다. 복제 할 경우 동등성 검사가 거짓을 반환하기 때문입니까?
Ajay Gaur

29
@AjayGaur이 답변은 JavaScript의 엄격한 평등 (===)을 이해하는 데 도움이 될 수 있지만 React의 shallowCompare () 함수에 대해서는 아무것도 알려주지 않습니다 (답변자가 귀하의 질문을 오해 한 것 같습니다). shallowCompare ()가 실제로 제공 한 문서에 있습니다. 비교중인 객체의 키를 반복하고 각 객체의 키 값이 완전히 같지 않을 때 true를 반환합니다. 여전히이 함수를 이해하지 못하고 상태를 변경하지 않아야하는 이유를 이해하지 못하는 경우 답변을 작성할 수 있습니다.
sunquan 2016-10-22

7
그것은 사실이 아닙니다. 이것 좀 봐. github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/…
Bright Lee

5
이 답변은 JS에서 같음 (==) 연산자와 완전 같음 (===) 연산자의 차이점을 설명합니다. 질문은 React에서 두 객체의 모든 props 간의 동등성을 확인하여 구현되는 얕은 비교에 관한 것입니다.
Mateo Hrastnik

@sunquan 그것에 대한 답변을 작성해 주시겠습니까?
Ajay Gaur

35

얕은 비교는 비교되는 객체의 속성이 "==="또는 완전 동일성을 사용하여 수행되고 속성에 대한 더 깊은 비교를 수행하지 않는 경우입니다. 예를 들어

// a simple implementation of the shallowCompare.
// only compares the first level properties and hence shallow.
// state updates(theoretically) if this function returns true.
function shallowCompare(newObj, prevObj){
    for (key in newObj){
        if(newObj[key] !== prevObj[key]) return true;
    }
    return false;
}
// 
var game_item = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
// Case 1:
// if this be the object passed to setState
var updated_game_item1 = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
shallowCompare(updated_game_item1, game_item); // true - meaning the state
                                               // will update.

두 객체가 모두 동일하게 보이지만은와 game_item.teams같은 참조가 아닙니다 updated_game_item.teams. 두 개체가 동일하려면 동일한 개체를 가리켜 야합니다. 따라서 상태가 업데이트되도록 평가됩니다.

// Case 2:
// if this be the object passed to setState
var updated_game_item2 = {
    game: "football",
    first_world_cup: "1930",
    teams: game_item.teams
}
shallowCompare(updated_game_item2, game_item); // false - meaning the state
                                               // will not update.

이번에는 새 개체와 이전 개체의 팀 속성이 동일한 개체를 가리 키므로 모든 속성이 엄격한 비교를 위해 true를 반환합니다.

// Case 3:
// if this be the object passed to setState
var updated_game_item3 = {
    first_world_cup: 1930
}
shallowCompare(updated_game_item3, game_item); // true - will update

updated_game_item3.first_world_cup속성하면서 1930 엄격한 평가는 숫자에 실패 game_item.first_world_cup문자열입니다. 비교가 느슨하다면 (==) 이것은 통과되었을 것입니다. 그럼에도 불구하고 이것은 상태 업데이트로 이어질 것입니다.

추가 사항 :

  1. 심층 비교를 수행하는 것은 상태 개체가 깊이 중첩 된 경우 성능에 상당한 영향을 미치므로 무의미합니다. 그러나 너무 중첩되지 않았고 여전히 깊은 비교가 필요한 경우 shouldComponentUpdate에서 구현하고 충분한 지 확인하십시오.
  2. 상태 객체를 직접 변경할 수는 있지만 반응하는 setState 메서드 흐름에서 구성 요소 업데이트주기 후크를 구현하기 때문에 구성 요소의 상태는 영향을받지 않습니다. 구성 요소 수명주기 후크를 의도적으로 피하기 위해 상태 개체를 직접 업데이트하는 경우 상태 개체가 아닌 데이터를 저장하는 데 간단한 변수 또는 개체를 사용해야 할 것입니다.

props를 통해 객체를 전달하거나 상태를 다음 상태와 비교하면 해당 객체의 속성이 변경 되더라도 여전히 동일한 객체를 가리 키므로 구성 요소가 다시 렌더링되지 않는다는 의미가 아닙니다. 거짓, 따라서 다시 렌더링하지 않습니까?
javascripting

@javascripting-이것이 변경하는 대신 객체가 변경 될 때 객체를 복제 (예 : Object.assign () 사용)하여 React가 언제 참조가 변경되고 구성 요소가 업데이트가 필요한지 알 수있는 이유입니다.
Mac_W

없는 prevObj키가 포함 된 경우 newObj비교가 실패합니다.
mzedeler

@mzedeler- "for in"이 prevObj가 아닌 newObj를 반복하기 때문에 그렇지 않습니다. 브라우저 개발자 콘솔에서있는 그대로 코드를 실행 해보십시오. 더욱이 얕은 비교의이 구현을 너무 심각하게 받아들이지 마십시오. 이것은 단지 개념을 보여주기위한 것입니다
supi

어레이는 어떻습니까?
Juan De la Cruz

27

얕은 비교는 문자열, 숫자와 같은 기본 유형의 경우 두 값이 동일한 지 확인하고 객체의 경우 참조 만 확인하여 작동합니다 . 따라서 깊은 중첩 개체를 얕게 비교하면 해당 개체 내부의 값이 아닌 참조 만 확인합니다.


11

React에서 얕은 비교에 대한 레거시 설명 도 있습니다 .

shallowCompare는 현재 상태 및 nextState 객체뿐 아니라 현재 props 및 nextProps 객체에 대해 얕은 동등성 검사를 수행합니다.

비교되는 객체의 키를 반복하고 각 객체의 키 값이 완전히 같지 않으면 true를 반환하여이를 수행합니다.

UPD : 현재 문서 는 얕은 비교에 대해 말합니다.

React 컴포넌트의 render () 함수가 동일한 props 및 state에서 동일한 결과를 렌더링하는 경우 경우에 따라 성능 향상을 위해 React.PureComponent를 사용할 수 있습니다.

React.PureComponent의 shouldComponentUpdate ()는 객체를 얕게 비교합니다. 여기에 복잡한 데이터 구조가 포함 된 경우 더 깊은 차이에 대해 거짓 음성을 생성 할 수 있습니다. 단순한 props 및 state를 기대할 때만 PureComponent를 확장하거나, 깊은 데이터 구조가 변경된 것을 알고있을 때 forceUpdate ()를 사용하십시오.

UPD2 : 저는 조정 이 얕은 비교 이해를위한 중요한 주제 라고 생각 합니다.


"거짓"이되어서는 안됩니다and returning true when the values
rahulg

2

키 가없는 경우 위의 @supi ( https://stackoverflow.com/a/51343585/800608 ) 의 얕은 등가 스 니펫이 실패합니다 . 이를 고려해야하는 구현은 다음과 같습니다.prevObjnewObj

const shallowEqual = (objA, objB) => {
  if (!objA || !objB) {
    return objA === objB
  }
  return !Boolean(
    Object
      .keys(Object.assign({}, objA, objB))
      .find((key) => objA[key] !== objB[key])
  )
}

위의 내용은 polyfill이없는 Explorer에서는 작동하지 않습니다.


좋아 보이지만이 경우 두 개의 NaN을 전달하면 false가 반환되지만 이전 답변에서는 true입니다.
Spadar Shut

0

예제가있는 구현이 있습니다.

const isObject = value => typeof value === 'object' && value !== null;

const compareObjects = (A, B) => {
  const keysA = Object.keys(A);
  const keysB = Object.keys(B);
 
  if (keysA.length !== keysB.length) {
    return false;
  }
 
  return !keysA.some(key => !B.hasOwnProperty(key) || A[key] !== B[key]);
};

const shallowEqual = (A, B) => {
  if (A === B) {
    return true;
  }
 
  if ([A, B].every(Number.isNaN)) {
    return true;
  }
  
  if (![A, B].every(isObject)) {
    return false;
  }
  
  return compareObjects(A, B);
};

const a = { field: 1 };
const b = { field: 2 };
const c = { field: { field: 1 } };
const d = { field: { field: 1 } };

console.log(shallowEqual(1, 1)); // true
console.log(shallowEqual(1, 2)); // false
console.log(shallowEqual(null, null)); // true
console.log(shallowEqual(NaN, NaN)); // true
console.log(shallowEqual([], [])); // true
console.log(shallowEqual([1], [2])); // false
console.log(shallowEqual({}, {})); // true
console.log(shallowEqual({}, a)); // false
console.log(shallowEqual(a, b)); // false
console.log(shallowEqual(a, c)); // false
console.log(shallowEqual(c, d)); // false

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.