React-Redux state not updating as expected—am I mutating reducer arguments?

Viewed 47

thanks in advance for your attention with this (I believe) very basic question. I'm working on building my first "full-stack" application, and am running into something I can't quite wrap my head around with React-Redux. A brief explanation of the project: users can submit band idea names, and up or down vote others' submissions. Now, I believe that my problem is I'm not interacting with the state appropriately in my reducer dealing with MODIFY_BAND_SCORE actions. Here's the git repository, and I'll also copy and paste my store reducers here:

export const store = createStore(
  combineReducers({
    bands(bands = defaultState.bands, action) {
      switch (action.type) {
        case mutations.CREATE_BAND:
          return [
            ...bands,
            {
              id: action.id,
              owner: action.owner,
              name: action.name,
              score: 0,
              flags: 0,
            },
          ];
        case mutations.MODIFY_BAND_SCORE:
          let targetBandIndex = bands.findIndex(
            (band) => band.id === action.bandID
          );
          let targetBand = bands.splice(targetBandIndex, 1)[0];
          targetBand.score = targetBand.score + action.value;
          bands.splice(targetBandIndex, 0, targetBand);
          return bands;
      }
      return bands;
    },
    users(users = defaultState.users, action) {
      return users;
    },
  }),
  applyMiddleware(createLogger(), sagaMiddleware)
);

Hopefully that's enough context to make informed suggestions about what's going on here—my apologies for not having a truly minimal working example for this! The behavior I'm seeing from Redux-Logger when I dispatch an action of type MODIFY_BAND_SCORE is that I am (in a way) seeing the change reflected in that the correct band is having its score modified by the correct amount, but it is showing somehow in the previous and next states! Here's a screenshot:

Screen shot of redux-logger output in browser console

I feel like I've maybe made this post longer than what it needs to be, am I correct in thinking that in my case for mutations.MODIFY_BAND_SCORE I'm actually modifying the state directly? This is probably occurring with my calling of .splice() on bands isn't it?

1 Answers

Like Siddharth mentioned,

let copyOfBands = [...bands]

will create a copy for you. It's important to remember that one of the key parts of Redux is that the store is read-only. It can be easy to forget that when dealing with non-primitive data (I've certainly done that a bunch), but you should always try to remember to make copies of the data, modify the copy, and then push the copy to store. This helps prevent you from getting really weird and hard to debug errors.

It is important to remember that the spread operator here will creates a shallow copy of the array, which means if you have other non-primitive objects inside the array (such as other arrays), you will have to copy those as well.

Related