¿Es esta la forma correcta de eliminar un artículo usando redux?


116

Sé que se supone que no debo mutar la entrada y debo clonar el objeto para mutarlo. Estaba siguiendo la convención utilizada en un proyecto de inicio redux que usaba:

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

para agregar un elemento: obtengo el uso de spread para agregar el elemento en la matriz.

para borrar utilicé:

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

pero esto está mutando el objeto de estado de entrada, ¿está prohibido aunque estoy devolviendo un nuevo objeto?


1
Pregunta rápida. Splice devuelve los elementos que eliminó. ¿Esa es tu intención? De lo contrario, debería utilizar slice, que también cumple con la ley de no mutaciones.
m0meni

Bueno, en este ejemplo estoy empalmando las dos secciones de la matriz en una nueva matriz, con el elemento que quería eliminar fuera. Slice también devuelve el artículo eliminado, ¿verdad? Solo que lo hace sin mutar la matriz original, por lo que sería el mejor enfoque.
CWright

@ AR7 según su sugerencia: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]usar cortar ahora en lugar de empalmar para no mutar la entrada: ¿es este el camino a seguir o hay una manera más concisa?
CWright

Respuestas:


207

No. Nunca mutes tu estado.

A pesar de que está devolviendo un objeto nuevo, todavía está contaminando el objeto anterior, lo que nunca querrá hacer. Esto hace que sea problemático al hacer comparaciones entre el estado antiguo y el nuevo. Por ejemplo, en el shouldComponentUpdateque react-redux se usa debajo del capó. También hace imposible viajar en el tiempo (es decir, deshacer y rehacer).

En su lugar, use métodos inmutables. Utilizar siempre Array#slicey nunca Array#splice.

Supongo de su código que action.payloades el índice del elemento que se elimina. Una mejor forma sería la siguiente:

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

esto no funciona si estamos tratando con el último elemento de la matriz, también el uso de ...en la segunda declaración duplicará el contenido de su estado
Thaenor

4
Demuestre eso con un ejemplo de jsfiddle / codepen. El fragmento arr.slice(arr.length)siempre debe producir una matriz vacía sin importar el contenido de arr.
David

1
@ david-l-walsh perdón por la confusión, debo haber cometido un error tipográfico o algo así al probar este ejemplo. Funciona de maravilla. Mi única pregunta es: ¿por qué la necesidad del operador de propagación ...en la segunda parte?...state.items.slice(action.payload + 1)
Thaenor

5
Array#slicedevuelve una matriz. Para combinar los dos cortes en una sola matriz, he usado el operador de extensión. Sin él, tendría una matriz de matrices.
David

4
eso tiene sentido. Muchas gracias por aclarar (y perdón por la confusión al principio).
Thaenor

149

Puede utilizar el método de filtro de matriz para eliminar un elemento específico de una matriz sin alterar el estado original.

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

En el contexto de su código, se vería así:

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

1
¿El filtro produce una nueva matriz?
Chenop

6
@chenop Sí, el método Array.filter devuelve una nueva matriz. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
Tenga en cuenta que si hay duplicados, esto eliminará TODOS. Para usar el filtro para eliminar un índice específico, puede usar, por ejemploarr.filter((val, i) => i !== action.payload )
erich2k8

21

El Array.prototype.filtermétodo ES6 devuelve una nueva matriz con los elementos que coinciden con los criterios. Por lo tanto, en el contexto de la pregunta original, esto sería:

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

.filter()no es un método ES2015, pero se ha agregado en la versión anterior ES5.
morkro

7

Otra variación del reductor inmutable "ELIMINADO" para la matriz con objetos:

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

0

La regla de oro es que no devolvemos un estado mutado, sino un nuevo estado. Dependiendo del tipo de su acción, es posible que deba actualizar su árbol de estado en varias formas cuando llegue al reductor.

En este escenario, estamos intentando eliminar un elemento de una propiedad estatal.

Esto nos lleva al concepto de patrones inmutables de actualización (o modificación de datos) de Redux. La inmutabilidad es clave porque nunca queremos cambiar directamente un valor en el árbol de estado, sino que siempre queremos hacer una copia y devolver un valor nuevo basado en el valor anterior.

A continuación, se muestra un ejemplo de cómo eliminar un objeto anidado:

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

Para comprender esto mejor, asegúrese de consultar este artículo: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.