Is this the correct way to delete an item using redux?

JavascriptReactjsRedux

Javascript Problem Overview


I know I'm not supposed to mutate the input and should clone the object to mutate it. I was following the convention used on a redux starter project which used:

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

for adding an item - I get the use of spread to append the item in the array.

for deleting I used:

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

but this is mutating the input state object - is this forbidden even though I am returning a new object?

Javascript Solutions


Solution 1 - Javascript

No. Never mutate your state.

Even though you're returning a new object, you're still polluting the old object, which you never want to do. This makes it problematic when doing comparisons between the old and the new state. For instance in shouldComponentUpdate which react-redux uses under the hood. It also makes time travel impossible (i.e. undo and redo).

Instead, use immutable methods. Always use Array#slice and never Array#splice.

I assume from your code that action.payload is the index of the item being removed. A better way would be as follows:

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

Solution 2 - Javascript

You can use the array filter method to remove a specific element from an array without mutating the original state.

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

In the context of your code, it would look something like this:

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

Solution 3 - Javascript

The ES6 Array.prototype.filter method returns a new array with the items that match the criteria. Therefore, in the context of the original question, this would be:

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

Solution 4 - Javascript

Another one variation of the immutable "DELETED" reducer for the array with objects:

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

Solution 5 - Javascript

The golden rule is that we do not return a mutated state, but rather a new state. Depending on the type of your action, you might need to update your state tree in various forms when it hits the reducer.

In this scenario we are trying to remove an item from a state property.

This brings us to the concept of Redux’s immutable update (or data modification) patterns. Immutability is key because we never want to directly change a value in the state tree, but rather always make a copy and return a new value based on the old value.

Here is an example of how to delete a nested object:

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

To understand this better, make sure to check out this article: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Solution 6 - Javascript

Deleting an item using redux in different ways.

> Method 1: In that case is used createSlice( .. )

const { id } = action.payload; // destruct id
removeCart: (state, action) =>{
                 let { id } = action.payload;
                 let arr = state.carts.filter(item => item.id !== parseInt(id))
                 state.carts = arr;
               }

> Method 2: In that case is used switch (... ), spread-operator

const { id } = action.payload; // destruct id

case actionTypes.DELETE_CART:  
     return {
        ...state,
        carts: state.carts.filter((item) => item.id !== payload)
      };

For both methods initialized this state:

  initialState: {
      carts: ProductData, // in productData mocked somedata     
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCWrightView Question on Stackoverflow
Solution 1 - JavascriptDavid L. WalshView Answer on Stackoverflow
Solution 2 - JavascriptSteph MView Answer on Stackoverflow
Solution 3 - JavascriptJoeTideeView Answer on Stackoverflow
Solution 4 - JavascriptRomanView Answer on Stackoverflow
Solution 5 - JavascriptKasra KhosraviView Answer on Stackoverflow
Solution 6 - JavascriptRigers LekaView Answer on Stackoverflow