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

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

我有我的减速器


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

我有派遣命令

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

/>

当我按下按钮时,我得到的是这个错误:

我已经隔离了所有内容以确保这是问题所在,而不是其他问题。

为什么会弹出这个错误?

问题是使用没有花括号的箭头函数作为缩减器,因为它充当隐式 return 语句。所以,你们都在改变 state.token return 赋值的结果。

根据 the Immer docs on returning data,有几种方法可以解决此问题:

  • 在赋值前添加void运算符
  • 将赋值包裹在大括号中使其成为函数体

所以 setToken reducer 可以更新为 void as

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

而不是

 state.token = action.payload.token

使用

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

常量是不可变的,将 action.payload 内容分配给常量解决了我的一个问题(请注意,如果负载是一个对象,则没有必要

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
  },
});

你可能会忘记休息;在您的 reducer.js 文件中声明;

 case LOGIN_USER_ERROR_RESET:
        draft.errormsg = '';
        draft.loading = false;
        draft.error = false;
        break;

要了解的要点IMMER

  1. 如果您 return 除了从配方函数中起草任何其他内容,该 return 值将替换状态
  2. 你主要想改变草稿来改变状态,**所以 return修改草稿后是可选的**,immer 无论如何都会 return 最终确定草稿
  3. 如果您 return 来自 recipe 函数的任何其他内容,那么根据第 1 点,新的 return 值将替换状态
  4. 第3点是可以的,前提是你的recipe函数中没有修改草稿。即你可以选择两者之一
    一个。修改草案 --> return draft 或 undefined,两者都可以
    b。想要 return 一些新值,那么根本不要碰草稿

现在来回答问题。 这是你的减速器功能

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

这个函数做两件事,
1。修改状态
2。 returning action.payload.test

所以它违反了第 4 点,因此来自 Immer 库的错误

在这种特定情况下,目的只是修改状态,所以我们必须用 void

撤消现有的 return
setToken: (state, action) => void(state.token = action.payload.test)

但是,Immer 库建议使用代码块来确保跨大型代码库的一致性,此代码块隐式 return未定义

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

尝试为 Copying Without Reference

检查此 link

正确答案

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
    },
});

对于那些使用 React PullState 的人,我的问题是语法:

我有:

 const onUpdate = (value: any) => {
    someStore.update((s) => (s.value = value));
 };

但必须是:

 const onUpdate = (value: any) => {
    someStore.update((s) => {
       s.value = value;
    });
 };

出于某种原因,不喜欢第一种方法中声明的箭头函数。