İd ile anahtarlanmış nesneye karşı nesne dizisi olarak durum


97

Durum Şeklini Tasarlama bölümünde , dokümanlar durumunuzu ID ile anahtarlanmış bir nesnede tutmanızı önerir:

Her varlığı anahtar olarak bir ID ile depolanan bir nesnede tutun ve diğer varlıklardan veya listelerden buna referans vermek için ID'leri kullanın.

Eyalete giderler

Uygulamanın durumunu bir veritabanı olarak düşünün.

Bazıları açılacak (bir açılır pencerede görüntüleniyorlar) veya seçili seçeneklere sahip olan bir filtre listesi için durum şekli üzerinde çalışıyorum. "Uygulamanın durumunu bir veritabanı olarak düşünün" ifadesini okuduğumda, bunları bir API'den (kendisi bir veritabanı tarafından desteklenen) döndürüleceği gibi bir JSON yanıtı olarak düşünmeyi düşündüm.

Ben de bunu şöyle düşünüyordum

[{
    id: '1',
    name: 'View',
    open: false,
    options: ['10', '11', '12', '13'],
    selectedOption: ['10'],
    parent: null,
  },
  {
    id: '10',
    name: 'Time & Fees',
    open: false,
    options: ['20', '21', '22', '23', '24'],
    selectedOption: null,
    parent: '1',
  }]

Ancak dokümanlar daha çok şuna benzer bir format önermektedir

{
   1: { 
    name: 'View',
    open: false,
    options: ['10', '11', '12', '13'],
    selectedOption: ['10'],
    parent: null,
  },
  10: {
    name: 'Time & Fees',
    open: false,
    options: ['20', '21', '22', '23', '24'],
    selectedOption: null,
    parent: '1',
  }
}

Teoride, veriler serileştirilebilir olduğu sürece ("Durum" başlığı altında) önemli olmamalıdır .

Bu yüzden, redüktörümü yazana kadar mutlu bir şekilde nesne dizisi yaklaşımına gittim.

Nesne anahtarlamalı kimlik yaklaşımı ile (ve yayılma sözdiziminin liberal kullanımı), OPEN_FILTERindirgeyicinin parçası

switch (action.type) {
  case OPEN_FILTER: {
    return { ...state, { ...state[action.id], open: true } }
  }

Nesne dizisi yaklaşımında ise, daha ayrıntılı (ve yardımcı işlev bağımlıdır)

switch (action.type) {
   case OPEN_FILTER: {
      // relies on getFilterById helper function
      const filter = getFilterById(state, action.id);
      const index = state.indexOf(filter);
      return state
        .slice(0, index)
        .concat([{ ...filter, open: true }])
        .concat(state.slice(index + 1));
    }
    ...

Yani sorularım üç yönlü:

1) Redüktörün basitliği, nesne-anahtarlı-kimlik yaklaşımıyla gitme motivasyonu mu? Bu devlet şeklinin başka avantajları var mı?

ve

2) Nesne anahtarlamalı kimlik yaklaşımı, bir API için standart JSON girişi / çıkışı ile başa çıkmayı zorlaştırıyor gibi görünüyor. (Bu yüzden ilk etapta nesneler dizisine gittim.) Yani bu yaklaşımı kullanırsanız, onu JSON formatı ve durum şekli formatı arasında ileri geri dönüştürmek için bir işlev mi kullanıyorsunuz? Bu hantal görünüyor. (Yine de bu yaklaşımı savunuyorsanız, bunun yukarıdaki nesneler dizisi indirgeyicisinden daha az hantal olduğu gerekçenizin bir parçası olabilir mi?)

ve

3) Dan Abramov'un teorik olarak durum veri yapısından bağımsız olacak şekilde yeniden tasarlandığını biliyorum ( "Geleneksel olarak, en üst düzey durum bir nesnedir veya Harita gibi başka bir anahtar-değer koleksiyonudur, ancak teknik olarak herhangi biri olabilir yazın " vurgu benim). Ancak yukarıdakilere bakıldığında, onu kimliğe göre anahtarlanmış bir nesne olarak tutmam "önerilir" mi, yoksa onu sadece iptal etmem gereken bir dizi nesne kullanarak karşılaşacağım öngörülemeyen başka sorunlar var mı? ID ile anahtarlanmış bir nesneyi planlayıp buna bağlı kalmaya mı çalışıyorsunuz?


2
Bu ilginç bir soru ve ben de biraz içgörü sağlamak için sordum, diziler yerine redux'de normalleştirme eğiliminde olsam da (sırf yukarı bakmanın daha kolay olması nedeniyle), normalleştirilmiş yaklaşımı alırsanız sıralamanın şöyle olduğunu görüyorum bir sorun çünkü dizinin size verdiği aynı yapıyı elde etmiyorsunuz, bu yüzden kendinizi sıralamak zorunda kalıyorsunuz.
Robert Saunders

'Nesne anahtarlamalı kimlik' yaklaşımında bir sorun görüyorum, ancak bu sık olmuyor ancak herhangi bir UI uygulaması yazarken bu durumu dikkate almalıyız. Peki ya sıralı liste olarak listelenen bir sürükle bırak öğesini kullanarak varlığın sırasını değiştirmek istersem? Genellikle 'nesne anahtarlamalı' yaklaşımı burada başarısız olur ve bu tür cömert sorunlardan kaçınmak için kesinlikle nesne yaklaşımı dizisi kullanırdım. Daha fazlası olabilirdi ama bunu burada paylaşmayı düşündüm
Kunal Navhate

Nesnelerden oluşan bir nesneyi nasıl sıralayabilirsiniz? Bu imkansız görünüyor.
David Vielhuber

@DavidVielhuber Lodash gibi bir şey kullanmak dışında mı demek istiyorsun sort_by? const sorted = _.sortBy(collection, 'attribute');
nickcoxdotme

Evet. Şu anda bu nesneleri bir vue hesaplamalı mülk içinde dizilere dönüştürüyoruz
David Vielhuber

Yanıtlar:


47

S1: İndirgeyicinin basitliği, doğru girişi bulmak için dizi içinde arama yapmak zorunda olmamanın bir sonucudur. Dizide arama yapmak zorunda olmamak avantajdır. Seçiciler ve diğer veri erişimcileri bu öğelere ile erişebilir ve sıklıkla erişir id. Her erişim için dizide arama yapmak zorunda kalmak bir performans sorunu haline gelir. Dizileriniz büyüdüğünde, performans sorunu hızla kötüleşir. Ayrıca, uygulamanız daha karmaşık hale geldikçe, verileri daha fazla yerde gösterip filtreledikçe, sorun daha da kötüleşir. Kombinasyon zararlı olabilir. Öğeleri erişerek idgelen erişim süresi değişiklikleri O(n)için O(1)büyük için, n(burada dizi öğe) büyük bir fark yaratıyor.

S2: normalizrAPI'den mağazaya dönüştürme konusunda size yardımcı olması için kullanabilirsiniz . Normalizr V3.1.0'dan itibaren, diğer yöne gitmek için denormalize kullanabilirsiniz. Bununla birlikte, Uygulamalar genellikle veri üreticilerinden daha fazla tüketicidir ve bu nedenle mağazaya dönüştürme genellikle daha sık yapılır.

S3: Bir dizi kullanarak karşılaşacağınız sorunlar, depolama kuralı ve / veya uyumsuzluklarla ilgili çok fazla sorun değil, daha fazla performans sorunudur.


normalizer, arka uçtaki tanımları değiştirdiğimizde kesinlikle acı yaratacak şeydir. Yani bunun her seferinde güncel kalması gerekiyor
Kunal Navhate

12

Uygulamanın durumunu bir veritabanı olarak düşünün.

Anahtar fikir bu.

1) Benzersiz kimliklere sahip nesnelere sahip olmak, nesneye referans verirken her zaman bu kimliği kullanmanıza olanak tanır, bu nedenle eylemler ve indirgeyiciler arasında minimum miktarda veri aktarmanız gerekir. Array.find (...) kullanmaktan daha etkilidir. Dizi yaklaşımını kullanırsanız, tüm nesneyi iletmeniz gerekir ve bu çok yakında dağınık hale gelebilir, nesneyi farklı indirgeyiciler, eylemler veya hatta kapta yeniden oluşturmaya başlayabilirsiniz (bunu istemezsiniz). Görünümler, ilişkili indirgeyicileri yalnızca kimliği içerse bile tam nesneyi her zaman alabilecektir, çünkü durumu eşlerken koleksiyonu bir yerde alırsınız (görünüm, tüm durumu mülklerle eşlemek için tüm durumu alır). Tüm söylediklerimden dolayı, eylemler minimum miktarda parametreye sahip olur ve minimum miktarda bilgiyi azaltır, bir deneyin,

2) API ile bağlantı, depolama ve azaltıcılarınızın mimarisini etkilememelidir, bu nedenle endişeleri birbirinden ayırmak için eylemleriniz vardır. Dönüşüm mantığınızı yeniden kullanılabilir bir modüle API'nin içine ve dışına koyun, bu modülü API'yi kullanan eylemlere içe aktarın ve bu olmalıdır.

3) Kimlikli yapılar için diziler kullandım ve bunlar yaşadığım öngörülemeyen sonuçlar:

  • Kod boyunca nesneleri sürekli olarak yeniden oluşturma
  • Gereksiz bilgileri düşürücülere ve eylemlere iletmek
  • Bunun sonucu olarak, kötü, temiz değil ve ölçeklenebilir olmayan kod.

Veri yapımı değiştirdim ve çok sayıda kodu yeniden yazdım. Uyarıldınız, lütfen başınızı belaya sokmayın.

Ayrıca:

4) Kimlikli koleksiyonların çoğu, kimliği tüm nesneye referans olarak kullanmak içindir, bundan yararlanmalısınız. API çağrıları kimliği ve ardından diğer parametreleri, eylemleriniz ve indirgeyicilerinizi alacak.


Redux mağazasındaki bir nesnede id tarafından depolanan çok sayıda veriye (1000 ila 10.000) sahip bir uygulamamız olduğu bir sorunla karşılaşıyorum. Görünümlerde, hepsi zaman serisi verilerini görüntülemek için sıralı dizileri kullanır. Bu, bir yeniden oluşturma işlemi her yapıldığında, tüm nesneyi alması, bir diziye dönüştürmesi ve sıralaması gerektiği anlamına gelir. Uygulamanın performansını iyileştirmekle görevlendirildim. Bu, verilerinizi sıralanmış bir dizide depolamanın ve bir nesne yerine silme ve güncellemeleri yapmak için ikili aramayı kullanmanın daha mantıklı olduğu bir kullanım durumu mu?
William Chou

Güncellemelerde hesaplama süresini en aza indirmek için bu verilerden türetilen başka karma haritalar yapmak zorunda kaldım. Bu, tüm farklı görünümlerin güncellenmesinin kendi güncelleme mantığını gerektirmesini sağlar. Bundan önce, tüm bileşenler nesneyi depodan alır ve görünümünü oluşturmak için ihtiyaç duyduğu veri yapılarını yeniden oluştururdu. Kullanıcı arayüzünde minimum sarsıntı sağlamak için düşünebileceğim tek yol, nesneden diziye dönüştürme yapmak için bir web çalışanı kullanmaktır. Bunun değiş tokuşu, daha basit erişim ve güncelleme mantığıdır çünkü tüm bileşenler, okunacak ve yazılacak tek bir veri türüne bağlıdır.
William Chou

8

1) Redüktörün basitliği, nesne-anahtarlı-kimlik yaklaşımıyla gitme motivasyonu mu? Bu devlet şeklinin başka avantajları var mı?

Varlıkları anahtar olarak ID'lerle depolanan nesnelerde tutmak istemenizin ana nedeni ( normalleştirilmiş olarak da adlandırılır ), derinlemesine iç içe geçmiş nesnelerle çalışmanın gerçekten zahmetli olmasıdır (genellikle daha karmaşık bir uygulamada REST API'lerinden elde ettiğiniz şey budur) - hem bileşenleriniz hem de redüktörleriniz için.

Mevcut örneğinizle normalleştirilmiş bir durumun faydalarını göstermek biraz zor (çünkü derinlemesine iç içe geçmiş bir yapıya sahip değilsiniz ). Ancak, seçeneklerin (örneğinizdeki) bir başlığı olduğunu ve sisteminizdeki kullanıcılar tarafından oluşturulduklarını varsayalım. Bu, yanıtın bunun yerine şöyle görünmesini sağlar:

[{
  id: 1,
  name: 'View',
  open: false,
  options: [
    {
      id: 10, 
      title: 'Option 10',
      created_by: { 
        id: 1, 
        username: 'thierry' 
      }
    },
    {
      id: 11, 
      title: 'Option 11',
      created_by: { 
        id: 2, 
        username: 'dennis'
      }
    },
    ...
  ],
  selectedOption: ['10'],
  parent: null,
},
...
]

Şimdi, seçenekleri oluşturan tüm kullanıcıların bir listesini gösteren bir bileşen oluşturmak istediğinizi varsayalım. Bunu yapmak için, önce tüm öğeleri istemeniz, ardından seçeneklerinin her birini yinelemeniz ve son olarak created_by.username öğesini almanız gerekir.

Daha iyi bir çözüm, yanıtı şu şekilde normalleştirmek olacaktır:

results: [1],
entities: {
  filterItems: {
    1: {
      id: 1,
      name: 'View',
      open: false,
      options: [10, 11],
      selectedOption: [10],
      parent: null
    }
  },
  options: {
    10: {
      id: 10,
      title: 'Option 10',
      created_by: 1
    },
    11: {
      id: 11,
      title: 'Option 11',
      created_by: 2
    }
  },
  optionCreators: {
    1: {
      id: 1,
      username: 'thierry',
    },
    2: {
      id: 2,
      username: 'dennis'
    }
  }
}

Bu yapıyla, seçenekleri oluşturan tüm kullanıcıları listelemek çok daha kolay ve daha etkilidir (onları entities.optionCreators'da izole ettik, bu yüzden sadece bu listeyi gözden geçirmeliyiz).

Aynı zamanda, örneğin ID 1'e sahip filtre öğesi için seçenekler yaratanların kullanıcı adlarını göstermek de oldukça basittir:

entities
  .filterItems[1].options
  .map(id => entities.options[id])
  .map(option => entities.optionCreators[option.created_by].username)

2) Nesne anahtarlamalı kimlik yaklaşımı, bir API için standart JSON girişi / çıkışı ile başa çıkmayı zorlaştırıyor gibi görünüyor. (Bu yüzden ilk etapta nesneler dizisine gittim.) Yani bu yaklaşımı kullanırsanız, onu JSON formatı ve durum şekli formatı arasında ileri geri dönüştürmek için bir işlev mi kullanıyorsunuz? Bu hantal görünüyor. (Yine de bu yaklaşımı savunuyorsanız, bunun yukarıdaki nesneler dizisi indirgeyicisinden daha az hantal olduğu gerekçenizin bir parçası olabilir mi?)

Bir JSON yanıtı, örneğin normalizr kullanılarak normalleştirilebilir .

3) Dan Abramov'un teorik olarak durum veri yapısı agnostiği olacak şekilde yeniden tasarladığını biliyorum ("Geleneksel olarak, en üst düzey durum bir nesne veya Harita gibi başka bir anahtar-değer koleksiyonudur, ancak teknik olarak herhangi biri olabilir yazın "vurgu benim). Ancak yukarıdakilere bakıldığında, onu kimliğe göre anahtarlanmış bir nesne olarak tutmam "önerilir" mi, yoksa onu sadece iptal etmem gereken bir dizi nesne kullanarak karşılaşacağım öngörülemeyen başka sorunlar var mı? ID ile anahtarlanmış bir nesneyi planlamak ve ona bağlı kalmaya çalışmak?

Muhtemelen, çok sayıda derinlemesine yuvalanmış API yanıtına sahip daha karmaşık uygulamalar için bir öneridir. Sizin özel örneğinizde, gerçekten o kadar önemli değil.


2
mapBurada olduğu gibi tanımsız döndürür , eğer kaynaklar ayrı olarak getirilirse, bu durum filterçok karmaşık hale gelir. Bir çözüm var mı?
Saravanabalagi Ramachandran

1
@tobiasandersen istemcinin dönüşümü normalizr gibi libler aracılığıyla yapmasını önlemek için sunucunun react / redux için ideal olan normalleştirilmiş verileri döndürmesinin uygun olduğunu düşünüyor musunuz? Başka bir deyişle, sunucunun verileri normalleştirmesini sağlayın, istemciyi değil.
Matthew
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.