Apakah ini cara yang benar untuk menghapus item menggunakan redux?


116

Saya tahu saya tidak seharusnya mengubah input dan harus mengkloning objek untuk mengubahnya. Saya mengikuti konvensi yang digunakan pada proyek pemula redux yang digunakan:

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

untuk menambahkan item - Saya menggunakan spread untuk menambahkan item dalam array.

untuk menghapus saya menggunakan:

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

tetapi ini mengubah objek status input - apakah ini dilarang meskipun saya mengembalikan objek baru?


1
Pertanyaan cepat. Splice mengembalikan item yang Anda hapus. Apa itu maksudmu? Jika tidak, Anda harus menggunakan slice, yang juga mematuhi hukum tanpa mutasi.
m0meni

Nah dalam contoh ini saya menyambung dua bagian dari array menjadi satu array baru - dengan item yang ingin saya hapus ditinggalkan. Slice juga mengembalikan item yang dihapus kan? Hanya itu yang melakukannya tanpa mengubah array asli sehingga akan menjadi pendekatan yang lebih baik?
CWright

@ AR7 sesuai saran Anda: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]gunakan slice sekarang sebagai ganti splice agar tidak mengubah input - apakah ini cara yang tepat atau ada cara yang lebih ringkas?
CWright

Jawaban:


207

Tidak. Jangan pernah mengubah status Anda.

Meskipun Anda mengembalikan objek baru, Anda masih mencemari objek lama, yang tidak ingin Anda lakukan. Ini membuatnya bermasalah saat melakukan perbandingan antara yang lama dan yang baru. Misalnya di shouldComponentUpdatemana react-redux menggunakan di bawah tenda. Itu juga membuat perjalanan waktu menjadi tidak mungkin (yaitu undo dan redo).

Sebaliknya, gunakan metode yang tidak dapat diubah. Selalu gunakan Array#slicedan jangan pernah Array#splice.

Saya berasumsi dari kode Anda itulah action.payloadindeks item yang dihapus. Cara yang lebih baik adalah sebagai berikut:

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

ini tidak berfungsi jika kita berurusan dengan elemen terakhir dalam larik, juga penggunaan ...dalam pernyataan kedua akan menggandakan konten negara Anda
Thaenor

4
Harap buktikan dengan contoh jsfiddle / codepen. Cuplikan arr.slice(arr.length)harus selalu menghasilkan larik kosong apa pun isinya arr.
David L. Walsh

1
@ david-l-walsh maaf atas kebingungannya, saya pasti salah ketik atau apalah saat menguji contoh ini. Ini bekerja dengan sangat baik. Satu-satunya pertanyaan saya adalah: mengapa perlu operator penyebaran ...di bagian kedua -...state.items.slice(action.payload + 1)
Thaenor

5
Array#slicemengembalikan array. Untuk menggabungkan dua irisan menjadi satu larik, saya telah menggunakan operator penyebaran. Tanpa itu, Anda akan memiliki array array.
David L. Walsh

4
itu masuk akal. Terima kasih banyak untuk klarifikasi (dan maaf atas kebingungan pada awalnya).
Thaenor

149

Anda dapat menggunakan metode filter larik untuk menghapus elemen tertentu dari larik tanpa mengubah status aslinya.

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

Dalam konteks kode Anda, akan terlihat seperti ini:

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

1
Apakah filter menghasilkan larik baru?
chenop

6
@chenop Ya, metode Array.filter mengembalikan larik baru. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
Perhatikan bahwa jika ada duplikat, ini akan menghapus SEMUAnya. Untuk menggunakan filter untuk menghapus indeks tertentu, Anda dapat menggunakan misalnyaarr.filter((val, i) => i !== action.payload )
erich2k8

21

Array.prototype.filterMetode ES6 mengembalikan larik baru dengan item yang cocok dengan kriteria. Oleh karena itu, dalam konteks pertanyaan awal, ini adalah:

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

.filter()bukan metode ES2015, tetapi telah ditambahkan di versi ES5 sebelumnya.
morkro

7

Variasi lain dari peredam "DELETED" yang tidak dapat diubah untuk larik dengan objek:

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

0

Aturan emasnya adalah bahwa kita tidak mengembalikan keadaan yang bermutasi, melainkan keadaan baru. Bergantung pada jenis tindakan Anda, Anda mungkin perlu memperbarui pohon status Anda dalam berbagai bentuk saat mencapai peredam.

Dalam skenario ini kami mencoba untuk menghapus item dari properti negara.

Hal ini membawa kita pada konsep pola pembaruan (atau modifikasi data) yang tidak dapat diubah dari Redux. Kekekalan adalah kuncinya karena kita tidak pernah ingin secara langsung mengubah nilai dalam pohon status, tetapi selalu membuat salinan dan mengembalikan nilai baru berdasarkan nilai lama.

Berikut adalah contoh cara menghapus objek bersarang:

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

Untuk memahami ini lebih baik, pastikan untuk membaca artikel ini: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.