È questo il modo corretto per eliminare un elemento utilizzando redux?


116

So che non dovrei cambiare l'input e dovrei clonare l'oggetto per modificarlo. Stavo seguendo la convenzione utilizzata su un progetto di avviamento redux che utilizzava:

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

per l'aggiunta di un elemento - ottengo l'uso di spread per aggiungere l'elemento nell'array.

per la cancellazione ho usato:

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

ma questo sta mutando l'oggetto dello stato di input - è vietato anche se sto restituendo un nuovo oggetto?


1
Domanda veloce. Splice restituisce gli elementi che hai rimosso. È questa la tua intenzione? In caso contrario, dovresti usare slice, che rispetta anche la legge sull'assenza di mutazioni.
m0meni

Bene, in questo esempio sto unendo le due sezioni dell'array insieme in un nuovo array, con l'elemento che volevo rimuovere lasciato fuori. Slice restituisce anche l'elemento rimosso, giusto? Solo che lo fa senza modificare l'array originale, quindi sarebbe l'approccio migliore?
CWright

@ AR7 come da tuo suggerimento: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]usare ora slice invece di splice in modo da non modificare l'input - è questa la strada da percorrere o c'è un modo più conciso?
CWright

Risposte:


207

No. Non mutare mai il tuo stato.

Anche se stai restituendo un nuovo oggetto, stai ancora inquinando il vecchio oggetto, cosa che non vorresti mai fare. Ciò rende problematico il confronto tra il vecchio e il nuovo stato. Ad esempio in shouldComponentUpdatecui React-Redux usa sotto il cofano. Inoltre rende impossibile viaggiare nel tempo (cioè annullare e ripetere).

Utilizza invece metodi immutabili. Usa sempre Array#slicee mai Array#splice.

Presumo dal tuo codice che action.payloadsia l'indice dell'articolo rimosso. Un modo migliore sarebbe il seguente:

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

questo non funziona se abbiamo a che fare con l'ultimo elemento dell'array, anche l'uso di ...nella seconda istruzione raddoppierà il contenuto del tuo stato
Thaenor,

4
Per favore dimostralo con un esempio jsfiddle / codepen. Lo snippet arr.slice(arr.length)dovrebbe sempre produrre un array vuoto indipendentemente dal contenuto di arr.
David L. Walsh,

1
@ david-l-walsh scusa per la confusione, devo aver fatto un errore di battitura o qualcosa del genere durante il test di questo esempio. Funziona a meraviglia. La mia unica domanda è: perché la necessità dello spread operator ...nella seconda parte -...state.items.slice(action.payload + 1)
Thaenor

5
Array#slicerestituisce un array. Per combinare le due sezioni in un unico array, ho utilizzato l'operatore di diffusione. Senza di esso, avresti un array di array.
David L. Walsh,

4
questo ha senso. Grazie mille per il chiarimento (e scusa per la confusione all'inizio).
Thaenor

149

È possibile utilizzare il metodo del filtro dell'array per rimuovere un elemento specifico da un array senza modificare lo stato originale.

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

Nel contesto del tuo codice, sarebbe simile a questo:

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

1
Il filtro produce un nuovo array?
chenop

6
@chenop Sì, il metodo Array.filter restituisce un nuovo array. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
Nota che se ci sono duplicati, questo li rimuoverà TUTTI. Per utilizzare il filtro per rimuovere un indice specifico, è possibile utilizzare ad esempioarr.filter((val, i) => i !== action.payload )
erich2k8

21

Il Array.prototype.filtermetodo ES6 restituisce un nuovo array con gli elementi che corrispondono ai criteri. Pertanto, nel contesto della domanda originale, questo sarebbe:

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

.filter()non è un metodo ES2015, ma è stato aggiunto nella versione precedente ES5.
morkro

7

Un'altra variante del riduttore immutabile "DELETED" per l'array con oggetti:

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

0

La regola d'oro è che non restituiamo uno stato mutato, ma piuttosto un nuovo stato. A seconda del tipo di azione, potrebbe essere necessario aggiornare l'albero degli stati in varie forme quando colpisce il riduttore.

In questo scenario stiamo tentando di rimuovere un elemento da una proprietà statale.

Questo ci porta al concetto di modelli di aggiornamento (o modifica dei dati) immutabili di Redux. L'immutabilità è la chiave perché non vogliamo mai cambiare direttamente un valore nell'albero degli stati, ma piuttosto sempre fare una copia e restituire un nuovo valore basato sul vecchio valore.

Ecco un esempio di come eliminare un oggetto nidificato:

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

Per capirlo meglio, assicurati di leggere questo articolo: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.