So sánh nông hoạt động như thế nào trong phản ứng


97

Trong tài liệu về React này, người ta nói rằng

AgriculturalCompare thực hiện kiểm tra bình đẳng nông trên các đối tượng props và nextProps hiện tại cũng như trạng thái hiện tại và các đối tượng nextState.

Điều mà tôi không thể hiểu được là Nếu nó so sánh nông cạn các đối tượng thì phương thức shouldComponentUpdate sẽ luôn trả về true, như

Chúng ta không nên thay đổi các trạng thái.

và nếu chúng ta không thay đổi trạng thái thì phép so sánh sẽ luôn trả về false và do đó, bản cập nhật shouldComponent sẽ luôn trả về true. Tôi bối rối về cách nó hoạt động và làm thế nào chúng tôi sẽ ghi đè điều này để tăng hiệu suất.

Câu trả lời:


132

So sánh nông không kiểm tra sự bình đẳng. Khi so sánh các giá trị vô hướng (số, chuỗi) nó sẽ so sánh các giá trị của chúng. Khi so sánh các đối tượng, nó không so sánh các thuộc tính của chúng - chỉ các tham chiếu của chúng được so sánh (ví dụ: "chúng có trỏ đến cùng một đối tượng không?).

Hãy xem xét hình dạng của uservật thể sau

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

Ví dụ 1:

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

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

Lưu ý rằng bạn đã thay đổi tên người dùng. Ngay cả với các đối tượng thay đổi này là bằng nhau. Họ tham khảo là hoàn toàn giống nhau.

Ví dụ 2:

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

Bây giờ, không có bất kỳ thay đổi nào đối với thuộc tính đối tượng, chúng hoàn toàn khác nhau. Bằng cách sao chép đối tượng gốc, bạn tạo ra bản sao mới, với các tham chiếu khác nhau.

Hàm sao chép có thể trông như thế này (cú pháp ES6)

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

So sánh nông là cách hiệu quả để phát hiện các thay đổi. Nó hy vọng bạn không thay đổi dữ liệu.


Vì vậy, Nếu chúng ta đang viết mã thì nếu chúng ta có các giá trị vô hướng thì chúng ta có nên thay đổi chúng không vì nếu chúng ta sao chép chúng, việc kiểm tra bình đẳng sẽ trả về false?
Ajay Bò tót

30
@AjayGaur Mặc dù câu trả lời này có thể giúp bạn hiểu sự bình đẳng nghiêm ngặt (===) trong JavaScript, nhưng nó không cho bạn biết bất cứ điều gì về hàm nôngCompare () trong React (tôi đoán người trả lời đã hiểu sai câu hỏi của bạn). Những gì nôngCompare () thực hiện nằm trong tài liệu bạn đã cung cấp: lặp lại các khóa của các đối tượng đang được so sánh và trả về true khi giá trị của một khóa trong mỗi đối tượng không hoàn toàn bằng nhau. Nếu bạn vẫn không hiểu hàm này và tại sao bạn không nên thay đổi trạng thái, tôi có thể viết câu trả lời cho bạn.
sunquan


5
Câu trả lời này mô tả sự khác biệt giữa toán tử bình đẳng (==) và bình đẳng nghiêm ngặt (===) trong JS. Câu hỏi là về so sánh nông, trong React được thực hiện bằng cách kiểm tra sự bằng nhau giữa tất cả các đạo cụ của hai đối tượng.
Mateo Hrastnik

@sunquan bạn có thể vui lòng viết câu trả lời trên đó được không?
Ajay Bò tót

35

So sánh nông là khi các thuộc tính của đối tượng được so sánh được thực hiện bằng cách sử dụng "===" hoặc bình đẳng nghiêm ngặt và sẽ không tiến hành so sánh sâu hơn vào các thuộc tính. ví dụ

// 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.

Mặc dù cả hai đối tượng có vẻ giống nhau, nhưng game_item.teamskhông phải là tham chiếu giống nhau updated_game_item.teams. Để 2 đối tượng giống nhau, chúng nên chỉ vào cùng một đối tượng. Do đó, điều này dẫn đến trạng thái được đánh giá là cập nhật

// 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.

Lần này mọi thuộc tính trả về true cho phép so sánh chặt chẽ vì thuộc tính nhóm trong đối tượng mới và cũ trỏ đến cùng một đối tượng.

// 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

Các updated_game_item3.first_world_cupbất động sản không đánh giá nghiêm ngặt như 1930 là một con số trong khi game_item.first_world_cuplà một chuỗi. Nếu sự so sánh bị lỏng lẻo (==) thì điều này sẽ trôi qua. Tuy nhiên, điều này cũng sẽ dẫn đến cập nhật trạng thái.

Ghi chú bổ sung:

  1. Thực hiện so sánh sâu là vô nghĩa vì nó sẽ ảnh hưởng đáng kể đến hiệu suất nếu đối tượng trạng thái được lồng sâu vào nhau. Nhưng nếu nó không quá lồng nhau và bạn vẫn cần so sánh sâu, hãy triển khai nó trong shouldComponentUpdate và kiểm tra xem điều đó có đủ hay không.
  2. Bạn chắc chắn có thể thay đổi đối tượng trạng thái trực tiếp nhưng trạng thái của các thành phần sẽ không bị ảnh hưởng, vì nó trong luồng phương thức setState mà phản ứng thực hiện các móc chu kỳ cập nhật thành phần. Nếu bạn cập nhật trực tiếp đối tượng trạng thái để cố tình tránh các móc vòng đời của thành phần, thì có lẽ bạn nên sử dụng một biến hoặc đối tượng đơn giản để lưu trữ dữ liệu chứ không phải đối tượng trạng thái.

Không có nghĩa là nếu tôi truyền một đối tượng qua đạo cụ hoặc so sánh trạng thái với trạng thái tiếp theo, thành phần sẽ không bao giờ hiển thị lại bởi vì ngay cả khi các thuộc tính của đối tượng đó đã thay đổi, nó vẫn sẽ trỏ đến cùng một đối tượng, do đó, dẫn đến false, do đó, không hiển thị lại?
javascripting

@javascripting - đó là lý do tại sao bạn sao chép (sử dụng ví dụ: Object.assign ()) các đối tượng của mình khi chúng thay đổi thay vì làm thay đổi chúng để React biết khi nào tham chiếu thay đổi và thành phần cần cập nhật.
Mac_W

Nếu prevObjchứa khóa newObjkhông có, quá trình so sánh sẽ không thành công.
mzedeler

@mzedeler - nó sẽ không vì "for in" lặp lại trên newObj chứ không phải trên presObj. thử chạy mã như trong bảng điều khiển dành cho nhà phát triển của trình duyệt. Hơn nữa, xin đừng coi việc triển khai so sánh nông cạn này quá nghiêm túc, đây chỉ là để chứng minh khái niệm
supi

những gì về mảng?
Juan De la Cruz

27

So sánh nông hoạt động bằng cách kiểm tra xem hai giá trị có bằng nhau không trong trường hợp các kiểu nguyên thủy như chuỗi, số và trong trường hợp đối tượng, nó chỉ kiểm tra tham chiếu . Vì vậy, nếu bạn so sánh nông một đối tượng lồng nhau sâu, nó sẽ chỉ kiểm tra tham chiếu chứ không phải các giá trị bên trong đối tượng đó.


11

Ngoài ra còn có lời giải thích kế thừa về so sánh nông trong React:

AgriculturalCompare thực hiện kiểm tra bình đẳng nông trên các đối tượng props và nextProps hiện tại cũng như trạng thái hiện tại và các đối tượng nextState.

Nó thực hiện điều này bằng cách lặp lại các khóa của các đối tượng được so sánh và trả về true khi giá trị của một khóa trong mỗi đối tượng không hoàn toàn bằng nhau.

UPD : Tài liệu hiện tại nói về so sánh nông:

Nếu hàm render () của thành phần React của bạn hiển thị cùng một kết quả với cùng một trạng thái và đạo cụ, bạn có thể sử dụng React.PureComponent để tăng hiệu suất trong một số trường hợp.

ShouldComponentUpdate () của React.PureComponent chỉ so sánh nông các đối tượng. Nếu chúng chứa các cấu trúc dữ liệu phức tạp, nó có thể tạo ra âm tính giả cho sự khác biệt sâu hơn. Chỉ mở rộng PureComponent khi bạn mong đợi có các đạo cụ và trạng thái đơn giản hoặc sử dụng forceUpdate () khi bạn biết cấu trúc dữ liệu sâu đã thay đổi

UPD2: Tôi nghĩ Hòa giải cũng là một chủ đề quan trọng để hiểu được so sánh nông cạn.


nó không phải là "false" trongand returning true when the values
rahulg

2

Đoạn mã bằng nông của @supi ở trên ( https://stackoverflow.com/a/51343585/800608 ) không thành công nếu prevObjcó khóa newObjkhông có. Đây là một cách triển khai cần tính đến điều đó:

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

Lưu ý rằng điều trên không hoạt động trong Explorer mà không có polyfills.


Có vẻ tốt nhưng trong trường hợp này, hai NaN trả về false trong khi câu trả lời trước đó là true.
Spadar Shut

0

Có một triển khai với các ví dụ.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.