Bagaimana dangkal membandingkan pekerjaan dalam bereaksi

97

Dalam dokumentasi React ini, disebutkan bahwa

shallowCompare melakukan pemeriksaan kesetaraan dangkal pada properti saat ini dan objek nextProps serta status saat ini dan objek nextState.

Hal yang tidak dapat saya pahami adalah Jika membandingkan objek secara dangkal maka metode shouldComponentUpdate akan selalu mengembalikan nilai true, karena

Kita seharusnya tidak mengubah negara bagian.

dan jika kita tidak memutasi statusnya maka perbandingan akan selalu mengembalikan false dan pembaruan shouldComponent akan selalu mengembalikan nilai true. Saya bingung tentang cara kerjanya dan bagaimana kita akan menimpanya untuk meningkatkan kinerja.

Ajay Gaur
sumber

Jawaban:

133

Perbandingan dangkal memeriksa kesetaraan. Saat membandingkan nilai skalar (angka, string), nilai tersebut akan dibandingkan. Ketika membandingkan objek, itu tidak membandingkan atributnya - hanya referensinya yang dibandingkan (misalnya "apakah mereka menunjuk ke objek yang sama?).

Mari pertimbangkan bentuk userobjek berikut

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

Contoh 1:

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

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

Perhatikan Anda mengubah nama pengguna. Bahkan dengan objek perubahan ini sama. Referensi mereka persis sama.

Contoh 2:

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

Sekarang, tanpa perubahan apa pun pada properti objek mereka benar-benar berbeda. Dengan mengkloning objek asli Anda membuat salinan baru, dengan referensi berbeda.

Fungsi klon mungkin terlihat seperti ini (sintaks ES6)

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

Perbandingan dangkal adalah cara yang efisien untuk mendeteksi perubahan. Ini berharap Anda tidak mengubah data.

Andreyco
sumber
Jadi jika kita menulis kode maka jika kita memiliki nilai skalar maka haruskah kita mutasi karena jika kita akan mengkloningnya, pemeriksaan kesetaraan akan menghasilkan nilai salah?
Ajay Gaur
30
@AjayGaur Meskipun jawaban ini dapat membantu Anda memahami kesetaraan ketat (===) dalam JavaScript, tetapi tidak memberi tahu Anda apa pun tentang fungsi shallowCompare () di React (saya kira penjawab salah memahami pertanyaan Anda). Apa yang shallowCompare () lakukan sebenarnya ada di dokumen yang Anda berikan: melakukan iterasi pada kunci objek yang dibandingkan dan mengembalikan true ketika nilai kunci di setiap objek tidak sama persis. Jika Anda masih tidak memahami fungsi ini dan mengapa Anda tidak boleh mengubah status, saya dapat menulis jawaban untuk Anda.
sunquan
7
Itu tidak benar. Lihat ini. github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/…
Bright Lee
5
Jawaban ini menjelaskan perbedaan antara operator persamaan (==) dan persamaan ketat (===) di JS. Pertanyaannya adalah tentang perbandingan dangkal yang, dalam React diimplementasikan dengan memeriksa kesetaraan antara semua props dari dua objek.
Mateo Hrastnik
@sunquan bisakah Anda menulis jawaban tentang itu?
Ajay Gaur
36

Perbandingan dangkal adalah ketika properti dari objek yang dibandingkan dilakukan menggunakan "===" atau persamaan yang ketat dan tidak akan melakukan perbandingan lebih dalam ke properti. untuk mis

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

Meskipun kedua objek tampak sama, game_item.teamsreferensi ini tidak sama dengan updated_game_item.teams. Agar 2 objek menjadi sama, mereka harus menunjuk ke objek yang sama. Dengan demikian ini menghasilkan status yang sedang dievaluasi untuk diperbarui

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

Kali ini setiap properti mengembalikan nilai true untuk perbandingan yang ketat karena properti tim di objek baru dan lama mengarah ke objek yang sama.

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

The updated_game_item3.first_world_cupproperti gagal evaluasi yang ketat seperti 1930 adalah angka sementara game_item.first_world_cupadalah string. Jika perbandingannya longgar (==) ini akan berlalu. Meskipun demikian ini juga akan menghasilkan pembaruan status.

Catatan tambahan:

  1. Melakukan perbandingan mendalam tidak ada gunanya karena akan memengaruhi performa secara signifikan jika objek status sangat bertingkat. Tetapi jika tidak terlalu bersarang dan Anda masih membutuhkan perbandingan yang mendalam, terapkan di shouldComponentUpdate dan periksa apakah itu sudah cukup.
  2. Anda pasti dapat mengubah objek status secara langsung tetapi status komponen tidak akan terpengaruh, karena dalam aliran metode setState yang bereaksi mengimplementasikan kait siklus pembaruan komponen. Jika Anda memperbarui objek status secara langsung untuk sengaja menghindari kait siklus-hidup komponen, maka mungkin Anda harus menggunakan variabel atau objek sederhana untuk menyimpan data dan bukan objek status.
supi
sumber
Bukankah itu berarti bahwa jika saya melewatkan suatu objek melalui props atau membandingkan status ke status berikutnya, komponen tidak akan pernah dirender ulang karena meskipun properti objek itu telah berubah, itu akan tetap mengarah ke objek yang sama, sehingga, menghasilkan false, jadi, tidak melakukan rendering ulang?
javascripting
@javascripting - itulah mengapa Anda akan mengkloning objek Anda (menggunakan misalnya Object.assign ()) saat objek berubah alih-alih memutasinya sehingga React akan tahu kapan referensi berubah dan komponen perlu diperbarui.
Mac_W
Jika prevObjberisi kunci yang newObjtidak memiliki, pembandingan akan gagal.
mzedeler
@mzedeler - tidak akan terjadi karena "untuk dalam" beriterasi pada newObj dan bukan pada prevObj. coba jalankan kode seperti yang ada di konsol pengembang browser. Selain itu tolong jangan menganggap penerapan shallow bandingkan ini terlalu serius, ini hanya untuk mendemonstrasikan konsepnya
supi
bagaimana dengan array?
Juan De la Cruz
27

Perbandingan dangkal bekerja dengan memeriksa apakah dua nilai sama dalam kasus tipe primitif seperti string, angka dan dalam kasus objek itu hanya memeriksa referensi . Jadi jika Anda dangkal membandingkan objek bersarang yang dalam, itu hanya akan memeriksa referensi bukan nilai di dalam objek itu.

akhil choudhary
sumber
11

Ada juga penjelasan warisan tentang shallow bandingkan di React:

shallowCompare melakukan pemeriksaan kesetaraan dangkal pada properti saat ini dan objek nextProps serta status saat ini dan objek nextState.

Ini dilakukan dengan melakukan iterasi pada kunci objek yang dibandingkan dan mengembalikan true ketika nilai kunci di setiap objek tidak benar-benar sama.

UPD : Dokumentasi saat ini mengatakan tentang perbandingan dangkal:

Jika fungsi render () komponen React Anda memberikan hasil yang sama dengan props dan status yang sama, Anda dapat menggunakan React.PureComponent untuk meningkatkan kinerja dalam beberapa kasus.

ShouldComponentUpdate () React.PureComponent hanya membandingkan objek secara dangkal. Jika ini berisi struktur data yang kompleks, ini mungkin menghasilkan negatif palsu untuk perbedaan yang lebih dalam. Perluas PureComponent hanya jika Anda berharap memiliki props dan status sederhana, atau gunakan forceUpdate () saat Anda mengetahui struktur data dalam telah berubah

UPD2: Menurut saya rekonsiliasi juga merupakan tema penting untuk dangkal membandingkan pemahaman.

valex
sumber
Bukankah seharusnya itu "salah" diand returning true when the values
rahulg
2

Cuplikan dangkal yang sama oleh @supi di atas ( https://stackoverflow.com/a/51343585/800608 ) gagal jika prevObjmemiliki kunci yang newObjtidak memilikinya. Berikut adalah implementasi yang harus memperhitungkannya:

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

Perhatikan bahwa hal di atas tidak berfungsi di Explorer tanpa polyfill.

mzedeler
sumber
Terlihat bagus tetapi dalam kasus ini melewatkan dua NaN mengembalikan salah sedangkan di jawaban sebelumnya itu benar.
Spadar Shut
0

Ada implementasi dengan contoh.

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

Max Starling
sumber