8

So, I'm new to redux toolkit.

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"}))}

/>

and 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?

Ivan Solobear
  • 95
  • 1
  • 5

4 Answers4

25

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)
Akshay Vijay Jain
  • 6,159
  • 4
  • 31
  • 34
markerikson
  • 42,022
  • 7
  • 87
  • 109
1

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 } 
Akshay Vijay Jain
  • 6,159
  • 4
  • 31
  • 34
0

Instead of

 state.token = action.payload.token

use

 const token = action.payload.token
 state.token = token

Constants are immutable, and assigning action.payload contents to a constant resolved a problem for me (Note that in case the payload is an object it is not necessary

Working example of createSlice:

import { createSlice } from '@reduxjs/toolkit';

export const slice = createSlice({
  name: 'login',
  initialState: {
      username: "Foo",
      password: "AAA",
      authorized : false
  },
  reducers: {
    setUsername: (state, action) => {

      const value = action.payload
      state.username = value // action.payload.username

    },
    setPassword: (state, action) => {

      const value = action.payload
      state.password = value // action.payload.password

    },
    logon: (state, action) => {

      state.username = action.payload.username
      state.password = action.payload.password
      state.authorized = true

    },
    logoff: state => state.authorized = false
  },
});
Yuri Gridin
  • 431
  • 3
  • 6
-2

you may forget break; statement at your reducer.js file;

 case LOGIN_USER_ERROR_RESET:
        draft.errormsg = '';
        draft.loading = false;
        draft.error = false;
        break;
Diana
  • 11
  • 1