20

EDIT: The solution is to return state after I replace it completely (return state = {...action.payload})! But why? I don't need to return it when I replace the fields individually.

I'm working with the Redux Toolkit, which simplifies some Redux boilerplate. One thing they do is use Immer to allow you to directly 'modify' state (in fact, you're not). It works fine, except I don't know how to replace my section of state entirely. For instance, I want to do something like this

const reducer = createReducer({ s: '', blip: [] }, {

    [postsBogus.type]: (state, action) => {
        state = { ...action.payload };
    }

but state remains untouched. Instead I have to do this

[postsBogus.type]: (state, action) => {
    state.s = action.payload.s;
    state.blip = action.payload.blip;
}

Is there a way I can replace state entirely?

Cerulean
  • 5,543
  • 9
  • 59
  • 111

2 Answers2

26

Yes, as you noted, you must return a new value to replace the state entirely.

Even in a "plain" Redux reducer, assigning state = newValue does nothing, because all that does is say that the local function variable named state is now pointing to a different value in memory. That does nothing to return a new value.

For Immer specifically, you can either mutate the contents of the Proxy-wrapped state value as long as it's an object or array, or you can return an entirely new value, but not both at once.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • I also ran into this trouble. Even after reading Immer's docs, I thought that doing `state = getInitialState()` from a slice reducer was safe, since it "feels" like I'm returning (or the slice will be implicitly returning) a new `state` value without doing any mutations to it (cause the mutations are implicitly done inside the new state value). I think this could be more clear in the docs. In regular Redux, I was always using `return` on every `switch-case` anyway, so this wasn't a problem (even when I was using Immer's `produce`). – cbdeveloper Sep 29 '20 at 12:27
  • This link is now dead, and I don't see anything to replace it in the immer docs. Is there no longer a supported way to do this? – erfling Mar 20 '21 at 22:03
  • Immer appears to have updated their docs structure to drop the `/docs/` portion from the URLs as part of a switch to Docusaurus v2, and that's broken their external links. Just replaced the Immer docs link in this post with the correct URL. – markerikson Mar 21 '21 at 15:19
  • 1
    Ran into this problem as well; the solution this helped with is `return {...action.payload}` – Max Pekarsky Sep 04 '21 at 20:30
  • 1
    @MaximP, you don't need the destructuring, just `return action.payload` is sufficient, as per the docs. – AntonOfTheWoods Jan 24 '22 at 13:07
6

You can, but not in this way, when you say:

function x(y){
   y = 4
}

You're mutating the function parameter, but not the state of redux, you have two options to update this state of your redux store:

either to set state.your_state_name = something or, in your case, what you want to do is to return a new object, the new object is what the new state value is going to be.

simple example:

myReducerFunc: (state, action) => {
  return {...action.payload }
},

another example:

const loggedInUserSlice = createSlice({
  name: '$loggedInUser',
  initialState: {
    isLoggedIn: false,
  },
  reducers: {
    loggedIn: (state, action) => {
      return {
        isLoggedIn: true,
        ...action.payload,
      }
    },
  },
})
Normal
  • 1,616
  • 15
  • 39