Yüzeysel karşılaştırma nasıl tepki verir?


97

Gelen bu belgelerin tepki nedeniyle, söylenir

sığCompare, mevcut props ve nextProps nesneleri ile mevcut durum ve nextState nesneleri üzerinde sığ bir eşitlik denetimi gerçekleştirir.

Anlayamadığım şey, nesneleri sığ bir şekilde karşılaştırırsa, shouldComponentUpdate yöntemi her zaman doğru döndürür, çünkü

Devletleri mutasyona uğratmamalıyız.

ve durumları değiştirmiyorsak, karşılaştırma her zaman yanlış döndürür ve bu nedenle shouldComponent güncellemesi her zaman doğru döndürür. Nasıl çalıştığı ve performansı artırmak için bunu nasıl geçersiz kılacağımız konusunda kafam karıştı.

Yanıtlar:


132

Sığ karşılaştırma eşitliği kontrol eder. Skaler değerleri karşılaştırırken (sayılar, dizeler) değerlerini karşılaştırır. Nesneleri karşılaştırırken, onların özniteliklerini karşılaştırmaz - sadece referansları karşılaştırılır (örneğin, "aynı nesneyi işaret ediyorlar mı?).

Aşağıdaki usernesnenin şeklini düşünelim

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

Örnek 1:

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

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

Kullanıcı adını değiştirdiğinize dikkat edin. Bu değişiklikle bile nesneler eşittir. Referanslar tamamen aynı.

Örnek 2:

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

Şimdi, nesne özelliklerinde herhangi bir değişiklik olmadan tamamen farklıdırlar. Orijinal nesneyi klonlayarak, farklı referanslarla yeni bir kopya oluşturursunuz.

Klon işlevi şöyle görünebilir (ES6 sözdizimi)

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

Sığ karşılaştırma, değişiklikleri saptamanın etkili bir yoludur. Verileri değiştirmemenizi bekler.


Öyleyse eğer kod yazıyorsak o zaman skaler değerlere sahipsek onları mutasyona uğratmalıyız çünkü onları klonlarsak, eşitlik kontrolü yanlış mı döndürür?
Ajay Gaur

30
@AjayGaur Bu cevap JavaScript'teki katı eşitliği (===) anlamanıza yardımcı olabilir, ancak React'teki sığCompare () işlevi hakkında size hiçbir şey söylemez (sanırım yanıtlayanın sorunuzu yanlış anladığı). SığCompare () işlevinin aslında sağladığınız belgede yaptığı şey: karşılaştırılan nesnelerin anahtarları üzerinde yineleme ve her nesnedeki bir anahtarın değerleri tam olarak eşit olmadığında true döndürme. Hala bu işlevi anlamıyorsanız ve durumu neden değiştirmemelisiniz, sizin için bir cevap yazabilirim.
sunquan


5
Bu cevap, JS'deki eşitlik (==) ve katı eşitlik (===) operatörleri arasındaki farkı açıklamaktadır. Soru, React'te iki nesnenin tüm sahne donanımları arasındaki eşitliğin kontrol edilmesiyle gerçekleştirilen yüzeysel karşılaştırmadır.
Mateo Hrastnik

@sunquan bunun üzerine bir cevap yazabilir misin?
Ajay Gaur

35

yüzeysel karşılaştırma, karşılaştırılan nesnelerin özelliklerinin "===" veya katı eşitlik kullanılarak yapıldığı ve özelliklerle ilgili daha derin karşılaştırmalar yapmayacağı zamandır. örneğin

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

Her iki nesne de aynı görünse de, game_item.teamsaynı referans değildirupdated_game_item.teams . 2 nesnenin aynı olması için aynı nesneyi göstermeleri gerekir. Böylece bu, değerlendirilen durumun güncellenmesine neden olur

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

Bu kez, yeni ve eski nesnedeki takımlar özelliği aynı nesneyi işaret ettiğinden, özelliklerin her biri katı karşılaştırma için true değerini döndürür.

// 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_cupMülkiyet iken 1930 kadar katı değerlendirme bir sayıdır başarısız game_item.first_world_cupbir dizedir. Karşılaştırma gevşek olsaydı (==) bu geçebilirdi. Bununla birlikte, bu durum aynı zamanda durum güncellemesine de neden olacaktır.

Ek Notlar:

  1. Durum nesnesi derinlemesine iç içe geçmişse performansı önemli ölçüde etkileyeceğinden derin karşılaştırma yapmak anlamsızdır. Ancak çok iç içe değilse ve yine de derin bir karşılaştırmaya ihtiyacınız varsa, bunu shouldComponentUpdate'e uygulayın ve bunun yeterli olup olmadığını kontrol edin.
  2. Durum nesnesini kesinlikle doğrudan değiştirebilirsiniz, ancak bileşenlerin durumu etkilenmeyecektir, çünkü tepki veren setState yöntemi akışında bileşen güncelleme döngüsü kancalarını uygular. Durum nesnesini, bileşen yaşam döngüsü kancalarından kasıtlı olarak kaçınmak için doğrudan güncellerseniz, muhtemelen verileri depolamak için durum nesnesini değil, basit bir değişken veya nesne kullanmalısınız.

Bu, bir nesneyi props aracılığıyla iletirsem veya durumu bir sonraki durumla karşılaştırırsam, bileşenin asla yeniden oluşturulmayacağı anlamına gelmez, çünkü o nesnenin özellikleri değişmiş olsa bile, yine de aynı nesneyi gösterecektir, dolayısıyla false, bu nedenle, yeniden oluşturma değil mi?
javascripting

@javascripting - işte bu yüzden nesnelerinizi değiştirdiklerinde değiştirdiklerinde değiştirdiklerinde (örneğin Object.assign () kullanarak) React referansın ne zaman değiştiğini ve bileşenin güncellenmesi gerektiğini bilecektir.
Mac_W

Eğer prevObjbir anahtarı içeren newObjyoktur, karşılaştırma başarısız olur.
mzedeler

@mzedeler - olmayacak çünkü "for in" prevObj'de değil newObj'de yineleniyor. kodu tarayıcı geliştirici konsolunda olduğu gibi çalıştırmayı deneyin. Üstelik lütfen bu sığ karşılaştırma uygulamasını fazla ciddiye almayın, bu sadece konsepti göstermek içindir
supi

diziler ne olacak?
Juan De la Cruz

27

Sığ karşılaştırma , dizge, sayılar gibi ilkel türlerde iki değerin eşit olup olmadığını kontrol ederek çalışır ve nesne durumunda sadece referansı kontrol eder . Dolayısıyla, derin yuvalanmış bir nesneyi sığ olarak karşılaştırırsanız, yalnızca o nesnenin içindeki değerleri değil referansı kontrol eder.


11

React'te sığ karşılaştırmanın eski açıklaması da vardır :

sığCompare, mevcut props ve nextProps nesneleri ile mevcut durum ve nextState nesneleri üzerinde sığ bir eşitlik denetimi gerçekleştirir.

Bunu, karşılaştırılan nesnelerin anahtarlarını yineleyerek ve her nesnedeki bir anahtarın değerleri tam olarak eşit olmadığında true döndürerek yapar.

UPD : Mevcut belgeler sığ karşılaştırma hakkında şunları söylüyor:

React bileşeninizin render () işlevi aynı props ve state verildiğinde aynı sonucu verirse, bazı durumlarda performans artışı için React.PureComponent'i kullanabilirsiniz.

React.PureComponent's shouldComponentUpdate () nesneleri yalnızca yüzeysel olarak karşılaştırır. Bunlar karmaşık veri yapıları içeriyorsa, daha derin farklılıklar için yanlış negatifler üretebilir. PureComponent'i yalnızca basit sahne ve duruma sahip olmayı beklediğinizde genişletin veya derin veri yapılarının değiştiğini bildiğinizde forceUpdate () kullanın

UPD2: Uzlaşmanın sığ karşılaştırma anlayışı için de önemli bir tema olduğunu düşünüyorum .


içinde "yanlış" olmamalıand returning true when the values
rahulg

2

Yukarıdaki @supi'nin sığ eşit parçacığı ( https://stackoverflow.com/a/51343585/800608 ) prevObj, olmayan bir anahtara sahipse başarısız olur newObj. İşte bunu dikkate alması gereken bir uygulama:

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

Yukarıdakilerin çoklu dolgular olmadan Explorer'da çalışmadığını unutmayın.


İyi görünüyor, ancak bu durumda iki NaN'yi geçmek yanlış döndürüyor, önceki cevapta bu doğru.
Spadar Shut

0

Örneklerle bir uygulama var.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.