从 producers 返回新数据

egghead.io 第9课: 返回全新 state
Hosted on egghead.io

不需要从 producer 那里返回任何东西,因为 Immer 无论如何都会返回 draft 的(最终)版本。但是,也允许仅仅 return draft

也允许从 producer 函数中任意返回其他数据。但前提是你没有修改 draft。这对于产生一个全新的 state 很有用。一些例子:

const userReducer = produce((draft, action) => {
switch (action.type) {
case "renameUser":
//可以: 我们修改了当前的 state
draft.users[action.payload.id].name = action.payload.name
return draft // 与仅仅 'return' 相同
case "loadUsers":
// 可以: 我们返回了一个全新的 state
return action.payload
case "adduser-1":
// 不行: 这不会改变 draft ,也不会返回新的状态
// 它不会修改 draft(它只是重新声明它)
// 事实上,这根本没有做任何事情
draft = {users: [...draft.users, action.payload]}
return
case "adduser-2":
// 不行: 修改 draft 的同时返回了一个新的状态
draft.userCount += 1
return {users: [...draft.users, action.payload]}
case "adduser-3":
// 可以: 返回一个新的状态。但是,不必要的复杂和昂贵
return {
userCount: draft.userCount + 1,
users: [...draft.users, action.payload]
}
case "adduser-4":
// 可以: immer 的方式
draft.userCount += 1
draft.users.push(action.payload)
return
}
})

注意:无法以这种方式返回 undefined ,因为它与不更新 draft 没有区别!继续阅读......

使用 nothing 产生 undefined#

因此,一般来说,可以通过从 producer 返回一个新值来替换当前 state,而不是修改 draft。然而,有一个微妙的边缘情况:如果您尝试编写一个想要用 undefined 替换当前状态的 producer:

produce({}, draft => {
// 什么也不干
})

或者:

produce({}, draft => {
// 尝试从 producer 中返回 undefined
return undefined
})

问题在于,在 JavaScript 中,一个不返回任何内容的函数也会返回 undefined!所以 immer 无法区分这些不同的情况。因此,默认情况下,Immer 会假设任何返回 undefined 的 producer 只是试图修改 draft。

但是,为了让 Immer 清楚您有意生成 undefined 值,您可以返回内置标记 nothing

import produce, {nothing} from "immer"
const state = {
hello: "world"
}
produce(state, draft => {})
produce(state, draft => undefined)
// 都会返回最初的状态: { hello: "world"}
produce(state, draft => nothing)
// 产生一个新的状态, 'undefined'

注:请注意,此问题特定于 undefined 值,任何其他值(包括 null)都不会受到此问题的影响

提示:为了能够在使用 TypeScript 时从 recipe 中返回 nothingstate 的类型必须接受 undefined 值。

使用 void 的内联快捷方式#

egghead.io 第10课: 使用 _void_ 避免意外的返回
Hosted on egghead.io

Immer 中的 draft 修改通常需要一段代码块,因为返回表示覆盖。有时候你可能觉得这么多的样板代码很糟心。

在这种情况下,您可以使用 javascripts void 运算符,它计算表达式并返回 undefined

// 单次修改
produce(draft => void (draft.user.age += 1))
// 多次修改
produce(draft => void ((draft.user.age += 1), (draft.user.height = 186)))

代码风格是高度个人化的,但对于要被许多人理解的代码库,我们建议坚持经典的 draft => { draft.user.age += 1} 以避免认知开销。