Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft

I have my reducer


const userAuthSlice = createSlice({
    name: "userAuth",
    initialState: {
        token: '',
    },
    reducers: {
        setToken: (state, action) => state.token = action.payload.test,
    },
});

And I have my dispatch command

<button
   value={text.sign_in.submit}
   onClick={() => dispatch(userAuthSlice.actions.setToken({test:"test"}))}

/>

As I press the button what I get is this error:

enter image description here

I have isolated everything to be sure that this is the problem and nothing else.

Why does this error pop up?

like image 538
Ivan Solobear Avatar asked Mar 22 '20 23:03

Ivan Solobear


3 Answers

The issue is the use of an arrow function with no curly braces as the reducer, because that acts as an implicit return statement. So, you're both mutating state.token, and returning the result of the assignment.

Per the Immer docs on returning data, there's a couple ways to fix this:

  • Adding the void operator in front of the assignment
  • Wrapping the assignment in curly braces to make it a function body

so setToken reducer can be updated with void as

setToken: (state, action) => void(state.token = action.payload.test)
like image 74
markerikson Avatar answered Nov 19 '22 01:11

markerikson


Points to learn about IMMER

  1. If you return anything other than draft from the recipe function, that return value will replace the state
  2. You mostly want to mutate the draft to mutate the state, ** so returning draft after modifying it is optional**, the immer will return finalized draft anyway
  3. if you return anything else from recipe function then as per point 1, the new return value will replace the state
  4. Point 3 is ok, only if you do not modify the draft in your recipe function. i.e. you can choose one of the two
    a. modify the draft --> return draft or undefined, either is ok
    b. want to return some new value, then don't touch the draft at all

Now coming to answer for the question. this is your reducer function

(state, action) => state.token = action.payload.test

This function is doing two things,
1. modifying state
2. returning action.payload.test

so it's violating point 4 and hence the error from Immer library

in this specific case, intention is only to modify the state, so we have to undo the existing return with void

setToken: (state, action) => void(state.token = action.payload.test)

However, Immer library recommends the use of code block to insure consistency across large code-base, this code block implicitly returns undefined

setToken: (state, action) => { state.token = action.payload.test } 
like image 7
Akshay Vijay Jain Avatar answered Nov 19 '22 01:11

Akshay Vijay Jain


Correct answer

Use Instead of

const userAuthSlice = createSlice({
    name: "userAuth",
    initialState: {
        token: '',
    },
    reducers: {
        setToken: (state, action) => state.token = action.payload.test,
    },
});

Use this {I have just added brackets}

const userAuthSlice = createSlice({
    name: "userAuth",
    initialState: {
        token: '',
    },
    reducers: {
        setToken: (state, action) => { state.token = action.payload.test}, // <<= Change Here
    },
});
like image 7
ZeevhY Org. Avatar answered Nov 19 '22 02:11

ZeevhY Org.