Ist dies der richtige Weg, um einen Artikel mit Redux zu löschen?

115

Ich weiß, dass ich die Eingabe nicht mutieren soll und das Objekt klonen soll, um es zu mutieren. Ich folgte der Konvention, die für ein Redux-Starterprojekt verwendet wurde, das Folgendes verwendete:

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

zum Hinzufügen eines Elements - Ich verwende die Spread-Funktion, um das Element im Array anzuhängen.

Zum Löschen habe ich verwendet:

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

Dies mutiert jedoch das Eingabestatusobjekt. Ist dies verboten, obwohl ich ein neues Objekt zurückgebe?

CWright
quelle
1
Schnelle Frage. Splice gibt die Elemente zurück, die Sie entfernt haben. Ist das deine Absicht? Wenn nicht, sollten Sie Slice verwenden, das auch das Gesetz ohne Mutationen einhält.
m0meni
Nun, in diesem Beispiel spleiße ich die beiden Abschnitte des Arrays zu einem neuen Array zusammen - wobei das Element, das ich entfernen wollte, weggelassen wurde. Slice gibt auch den entfernten Artikel zurück, oder? Nur ohne das ursprüngliche Array zu mutieren, wäre dies der bessere Ansatz?
CWright
@ AR7 gemäß Ihrem Vorschlag: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]Verwenden Sie jetzt Slice anstelle von Splice, um die Eingabe nicht zu mutieren. Ist dies der richtige Weg oder gibt es einen präziseren Weg?
CWright

Antworten:

205

Mutiere niemals deinen Zustand.

Auch wenn Sie ein neues Objekt zurückgeben, verschmutzen Sie immer noch das alte Objekt, was Sie niemals tun möchten. Dies macht es problematisch, Vergleiche zwischen dem alten und dem neuen Zustand anzustellen. Zum Beispiel bei shouldComponentUpdatedem React-Redux unter der Haube eingesetzt wird. Es macht auch Zeitreisen unmöglich (dh rückgängig machen und wiederholen).

Verwenden Sie stattdessen unveränderliche Methoden. Immer benutzen Array#sliceund niemals Array#splice.

Ich gehe von Ihrem Code aus, dass dies action.payloadder Index des Elements ist, das entfernt wird. Ein besserer Weg wäre wie folgt:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],
David L. Walsh
quelle
Dies funktioniert nicht, wenn es sich um das letzte Element im Array handelt. Auch die Verwendung ...in der zweiten Anweisung verdoppelt den Inhalt Ihres Status
Thaenor
4
Bitte beweisen Sie dies anhand eines Beispiels für jsfiddle / codepen. Das Snippet arr.slice(arr.length)sollte immer ein leeres Array erzeugen, unabhängig vom Inhalt arr.
David L. Walsh
1
@ david-l-walsh Entschuldigung für die Verwirrung, ich muss einen Tippfehler oder etwas gemacht haben, als ich dieses Beispiel getestet habe. Es wirkt Wunder. Meine einzige Frage ist: Warum braucht der Spread-Operator ...im zweiten Teil -...state.items.slice(action.payload + 1)
Thaenor
5
Array#slicegibt ein Array zurück. Um die beiden Slices zu einem einzigen Array zu kombinieren, habe ich den Spread-Operator verwendet. Ohne sie hätten Sie ein Array von Arrays.
David L. Walsh
4
Das macht Sinn. Vielen Dank für die Klarstellung (und entschuldigen Sie zunächst die Verwirrung).
Thaenor
149

Mit der Array-Filtermethode können Sie ein bestimmtes Element aus einem Array entfernen, ohne den ursprünglichen Status zu ändern.

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

Im Kontext Ihres Codes würde es ungefähr so ​​aussehen:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})
Steph M.
quelle
1
Erzeugt der Filter ein neues Array?
Chenop
6
@chenop Ja, die Array.filter-Methode gibt ein neues Array zurück. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M
4
Beachten Sie, dass bei Duplikaten ALLE entfernt werden. Um Filter zu verwenden, um einen bestimmten Index zu entfernen, können Sie zBarr.filter((val, i) => i !== action.payload )
erich2k8
21

Die ES6- Array.prototype.filterMethode gibt ein neues Array mit den Elementen zurück, die den Kriterien entsprechen. Im Kontext der ursprünglichen Frage wäre dies daher:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})
JoeTidee
quelle
.filter()ist keine ES2015-Methode, wurde jedoch in der früheren Version ES5 hinzugefügt.
Morkro
7

Eine weitere Variante des unveränderlichen "DELETED" -Reduzierers für das Array mit Objekten:

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

Die goldene Regel ist, dass wir keinen mutierten Zustand zurückgeben, sondern einen neuen Zustand. Abhängig von der Art Ihrer Aktion müssen Sie möglicherweise Ihren Statusbaum in verschiedenen Formen aktualisieren, wenn er auf den Reduzierer trifft.

In diesem Szenario versuchen wir, ein Element aus einer Statuseigenschaft zu entfernen.

Dies bringt uns zum Konzept der unveränderlichen Update- (oder Datenmodifikations-) Muster von Redux. Unveränderlichkeit ist der Schlüssel, da wir niemals einen Wert im Statusbaum direkt ändern möchten, sondern immer eine Kopie erstellen und einen neuen Wert basierend auf dem alten Wert zurückgeben möchten.

Hier ist ein Beispiel zum Löschen eines verschachtelten Objekts:

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

Lesen Sie diesen Artikel, um dies besser zu verstehen: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Kasra Khosravi
quelle