Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A case reducer on a non-draftable value must not return undefined

I'm working on my project @reduxjs/toolkit and created UserSlice like below.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { User } from "../../models";

export type UserState = User | null;

export const initialUserState: UserState = null;

const UserSlice = createSlice({
  name: "User",
  initialState: initialUserState,
  reducers: {
    receiveUser: (state: UserState, action: PayloadAction<UserState>) => {
      state = action.payload;
    },
    clearUser: (state: UserState, _action: PayloadAction<void>) => {
      state = null;
    },
    signIn: (
      _,
      _action: PayloadAction<{
        email: string;
        password: string;
      }>
    ) => {},
    signInWithToken: (_, _action: PayloadAction<void>) => {},
  },
});

export default UserSlice;

signIn signInWithToken are action creators just for taking actions in UserSaga module, so they do nothing.

When I dispatch signIn action, below error occurs.

index.js:1 Error: A case reducer on a non-draftable value must not return undefined
    at createReducer.ts:163
    at Array.reduce (<anonymous>)
    at createReducer.ts:143
...

This code snippets pattern works fine my other projects, but this project. I can't figure out the reason of this error, so I'm asking this question. Are there any faults in my code?

like image 245
Benjamin Avatar asked Dec 07 '22 10:12

Benjamin


2 Answers

Redux Toolkit's createReducer() allows writing reducers that directly mutate the state. This works by wrapping the reducer call with produce from the Immer library.

However, the reducer call isn't wrapped with produce when the current state isn't "draftable" by Immer, which is the case for primitive values, including null:

} else if (!isDraftable(previousState)) {
  // If state is not draftable (ex: a primitive, such as 0), we want to directly
  // return the caseReducer func and not wrap it with produce.
  const result = caseReducer(previousState as any, action)

  if (typeof result === 'undefined') {
    throw Error(
      'A case reducer on a non-draftable value must not return undefined'
    )
  }

  return result
}

Because your initial User state is null, you must return the new state from your receiveUser() reducer.

like image 156
Max Smolens Avatar answered Dec 31 '22 03:12

Max Smolens


I believe you're not returning the state in all of your reducers, because when using arrow functions objects need additional round brackets:

  reducers: {
    receiveUser: (state: UserState, action: PayloadAction<UserState>) => ({
      state: action.payload;
    }),
    clearUser: (state: UserState, _action: PayloadAction<void>) => ({
      state : null;
    }),
    signIn: (
      _,
      _action: PayloadAction<{
        email: string;
        password: string;
      }>
    ) => ({}),
    signInWithToken: (_, _action: PayloadAction<void>) => ({}),
  },
like image 45
Melchia Avatar answered Dec 31 '22 02:12

Melchia