Redux kullanarak bir öğeyi silmenin doğru yolu bu mu?


117

Girişi mutasyona uğratmamam gerektiğini ve onu değiştirmek için nesneyi klonlamam gerektiğini biliyorum. Redux başlangıç ​​projesinde kullanılan kongreyi takip ediyordum ve kullanılan:

ADD_ITEM: (state, action) => ({
  ...state,
  items: [...state.items, action.payload.value],
  lastUpdated: action.payload.date
})

bir öğe eklemek için - Öğeyi diziye eklemek için spread kullanımını alıyorum.

silmek için kullandım:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
  lastUpdated: Date.now() 
})

ancak bu, giriş durumu nesnesini değiştiriyor - yeni bir nesne döndürmeme rağmen bu yasak mı?


1
Hızlı soru. Splice, kaldırdığınız öğeleri döndürür. Niyetin bu mu? Değilse, mutasyon yok yasasına da uyan dilim kullanmalısınız.
m0meni

Bu örnekte, dizinin iki bölümünü yeni bir diziye ekliyorum - çıkarmak istediğim öğe dışarıda bırakılarak. Dilim ayrıca kaldırılan öğeyi döndürür değil mi? Ancak bunu orijinal diziyi değiştirmeden yapar, böylece daha iyi bir yaklaşım olur?
CWright

@ AR7 önerinize göre: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]girişi değiştirmemek için ekleme yerine şimdi dilim kullanmak - bu yol mu yoksa daha kısa bir yol mu var?
CWright

Yanıtlar:


208

Hayır. Durumunuzu asla değiştirmeyin.

Yeni bir nesneyi iade etseniz bile, asla yapmak istemediğiniz eski nesneyi kirletiyorsunuz. Bu, eski ve yeni durum arasında karşılaştırma yaparken sorunlu hale getirir. Örneğin shouldComponentUpdatereact-redux, kaputun altında kullanılır. Ayrıca zaman yolculuğunu imkansız kılar (yani geri al ve yinele).

Bunun yerine, değişmez yöntemler kullanın. Daima kullanın Array#sliceve asla Array#splice.

Kodunuzdan action.payloadkaldırılan öğenin dizini olduğunu varsayıyorum . Daha iyi bir yol şu şekilde olabilir:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],

...
Dizideki

4
Lütfen bunu bir jsfiddle / codepen örneği ile kanıtlayın. Parçacık arr.slice(arr.length), içeriği ne olursa olsun her zaman boş bir dizi oluşturmalıdır arr.
David

1
@ david-l-walsh karışıklık için özür dilerim, bu örneği test ederken bir yazım hatası yapmış olmalıyım. Harikalar yaratıyor. Benim tek sorum şu: neden ...ikinci bölümdeki yayılma operatörünün ihtiyacı -...state.items.slice(action.payload + 1)
Thaenor

5
Array#slicebir dizi döndürür. İki dilimi tek bir dizide birleştirmek için, yayılma operatörünü kullandım. Onsuz, bir dizi diziniz olur.
David

4
mantıklı. Açıklığa kavuşturduğunuz için çok teşekkür ederim (ve başta karışıklık için özür dilerim).
Thaenor

150

Orijinal durumu değiştirmeden belirli bir öğeyi bir diziden kaldırmak için dizi filtresi yöntemini kullanabilirsiniz.

return state.filter(element => element !== action.payload);

Kodunuz bağlamında şuna benzer:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})

1
Filtre yeni bir dizi mi üretir?
chenop

6
@chenop Evet, Array.filter yöntemi yeni bir dizi döndürür. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
Yinelenenler varsa, bunların TÜMÜNÜ kaldıracağını unutmayın. Belirli bir dizini kaldırmak için filtreyi kullanmak için örneğinarr.filter((val, i) => i !== action.payload )
erich2k8

22

ES6 Array.prototype.filteryöntemi, ölçütlerle eşleşen öğelerle yeni bir dizi döndürür. Bu nedenle, orijinal soru bağlamında bu şöyle olacaktır:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})

.filter()bir ES2015 yöntemi değildir, ancak önceki ES5 sürümüne eklenmiştir.
morkro

7

Nesneli dizi için değişmez "SİLİNMİŞ" azaltıcının bir başka çeşidi:

const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
  ...state.slice(0, index),
  ...state.slice(index + 1)
];
return stateTemp;

0

Altın kural, mutasyona uğramış bir durumu değil, yeni bir durumu döndürmemizdir. Eyleminizin türüne bağlı olarak, azaltıcıya çarptığında durum ağacınızı çeşitli şekillerde güncellemeniz gerekebilir.

Bu senaryoda bir öğeyi bir durum özelliğinden kaldırmaya çalışıyoruz.

Bu bizi Redux'un değişmez güncelleme (veya veri modifikasyonu) kalıpları kavramına getiriyor. Değişmezlik anahtardır, çünkü durum ağacındaki bir değeri asla doğrudan değiştirmek istemeyiz, bunun yerine her zaman bir kopya oluşturup eski değeri temel alan yeni bir değer döndürmek istemeyiz.

İç içe geçmiş bir nesnenin nasıl silineceğine dair bir örnek:

// ducks/outfits (Parent)

// types
export const NAME = `@outfitsData`;
export const REMOVE_FILTER = `${NAME}/REMOVE_FILTER`;

// initialization
const initialState = {
  isInitiallyLoaded: false,
  outfits: ['Outfit.1', 'Outfit.2'],
  filters: {
    brand: [],
    colour: [],
  },
  error: '',
};

// action creators
export function removeFilter({ field, index }) {
  return {
    type: REMOVE_FILTER,
    field,
    index,
  };
}

export default function reducer(state = initialState, action = {}) {
  sswitch (action.type) {  
  case REMOVE_FILTER:
  return {
    ...state,
    filters: {
    ...state.filters,
       [action.field]: [...state.filters[action.field]]
       .filter((x, index) => index !== action.index)
    },
  };
  default:
     return state;
  }
}

Bunu daha iyi anlamak için şu makaleye göz attığınızdan emin olun: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

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.