Using splice in reducer (redux)

Viewed 3518

I'm building my first application with React and Redux. I'm trying to delete an item from an array in my reducer using splice.

Here my state:

  favourite: { recipes: [], wines: [], products: [] }

Here my action:

  if (action.type === "REMOVE_RECIPE_FROM_FAV") {
    let favourite = state.favourite;
    favourite.recipes.splice(action.data, 1);
    return {
      ...state,
      favourite: favourite,
    };
  }

When i console.log favourite i can see that I'm deleting the item, but my component doesn't update.

Could you please help me?

3 Answers

You have to take care of the immutibility of the redux state. You need to make a copy of the recipes array and change it and merge with the state without affecting the others.

if (action.type === "REMOVE_RECIPE_FROM_FAV") {
    let favourite = state.favourite;
    const cloneRecipes = [...favourite.recipes];
    cloneRecipes.splice(action.data, 1);

    return {
        ...state,
        favourite: {
            ...state.favourite,
            recipes: cloneRecipes
        }
    }
}

Note that, the [...favourite.recipes] makes a shallow copy of the recipes array. For a nested array, it does not help you.

I suggest you to use Immutability Helpers library instead. By using this library the same task could be done like:

if (action.type === "REMOVE_RECIPE_FROM_FAV") {
    return update(state, {
        favourite: {
            recipes: {$splice: [[action.data, 1]]}
        }
    });
}

As Robin Zigmond said that you must return a new state from reducer not mutate it so instead use array.filter to remove the action.data from the favourite array.

    let favourite = state.favourite;
    // this return new items array removing action.data from it.
    let newFavouriteArray = favourite.filter(item => item !== action.data)
    return {
      ...state,
      favourite: newFavouriteArray,
    };

Your reducers should be pure functions, meaning that they should not mutate the state passed in.

However, here you are modifying the state, since the favourite variable that you create does not contain a copy of the favourite object in your state, but just a reference to it. Therefore, when you use splice, you are actually modifying the original recipes array, not creating a new one, so Redux is not able to detect that it has changed, since it's actually the same object!

To avoid this problem, you could do something like this:

if (action.type === "REMOVE_RECIPE_FROM_FAV") {
  return {
    ...state,
    favourite: {...favourite, 
                recipes: { state.favourite.recipes.slice(action.data, action.data + 1) }
    }
  };
}

I'm assuming that action.data contains the index of the element to remove.

Related