4

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?

Davide
  • 77
  • 2
  • 4
  • do you mind to show code where you are getting and using this `favourite` variable? – Uma May 09 '20 at 13:06
  • 8
    reducers must return new data, not merely mutate it. `splice` mutates. As far as Redux is concerned, `favourite` has not changed, as the reference is still the same. – Robin Zigmond May 09 '20 at 13:08
  • I agree with @RobinZigmond so you must spread into a new array, see my answer below. – Fasani May 09 '20 at 13:13
  • Does this answer your question? [Is this the correct way to delete an item using redux?](https://stackoverflow.com/questions/34582678/is-this-the-correct-way-to-delete-an-item-using-redux) – wentjun May 09 '20 at 13:20

3 Answers3

3

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]]}
        }
    });
}
Sajeeb Ahamed
  • 6,070
  • 2
  • 21
  • 30
  • Instead of immutability helpers addition you can upgrade whole Redux processing to use Redux Toolkit (RTK). It uses Immer internally and you can use splice in implementation while RTK actually immutably updates detecting the update and applies Immer on top of it. This is certainly advanced knowledge, and you should learn more, especially if using TypeScript, this course is great option https://egghead.io/courses/modern-redux-with-redux-toolkit-rtk-and-typescript-64f243c8 – Erkka Mutanen Oct 12 '21 at 10:47
0

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,
    };
0

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.

Tao Gómez Gil
  • 2,228
  • 20
  • 36