陷阱

性能提示#

对于性能提示,阅读 性能提示.

不要重新分配 recipe 参数#

永远不要重新分配 draft 参数(例如:draft = myCoolNewState)。相反,要么修改 draft,要么返回新状态。请参阅从 producers 返回数据

Immer 只支持单向树#

Immer 假设您的状态是单向树。也就是说,任何对象都不应该在树中出现两次,也不应该有循环引用。从根到树的任何节点应该只有一条路径。

永远不要从 producer 那里显式返回 undefined#

可以从 producers 返回值,但不能以这种方式返回 undefined,因为它与根本不更新 draft 没有区别!如果你想用 undefined 替换 draft,只需从 producer 那里返回 nothing

不要修改特殊对象#

Immer 不支持特殊对象 比如 window.location.

类应该是可 draft 的或不可变的#

您将需要使自己的类能与 Immer 一起正常工作。有关该主题的文档,请查看有关使用复杂对象的部分。

只有有效的索引和长度可以在数组上改变#

对于数组,只能改变数值属性和 length 属性。自定义属性不会保留在数组上。

只有来自 state 的数据会被 draft#

请注意,来自闭包而不是来自基本 state 的数据将永远不会被 draft,即使数据已成为新 darft 的一部分。

function onReceiveTodo(todo) {
const nextTodos = produce(todos, draft => {
draft.todos[todo.id] = todo
// 注意,因为 todo 来自外部,而不是 draft,所以他不会被 draft,
// 所以下面的修改会影响原来的 todo!
draft.todos[todo.id].done = true
// 上面的代码相当于
todo.done = true
draft.todos[todo.id] = todo
})
}

Immer patches 不一定是最优的#

Immer 生成的 patches 应该是正确的,也就是说,将它们应用于相同的基础对象应该会导致相同的最终状态。然而,Immer 不保证生成的 patches 是最优的,即可能的最小 patches

始终使用嵌套 producers 的结果#

支持嵌套调用 produce ,但请注意 produce始终产生新状态,因此即使将 draft 传递给嵌套 produce,内部 produce 所做的更改也不会在传递给它的 draft 中可见,只会反映在产生的输出中。换句话说,当使用嵌套 produce 时,您会得到 draft 的 draft,并且内部 produce 的结果应该合并回原始 draft(或返回)。例如,如果内部 produce 的输出没有被使用的话, produce(state, draft => {produce(draft.user, userDraft => { userDraft.name += "!" })}) 将不会生效。使用嵌套 producers 的正确方法是:

produce(state, draft => {
draft.user = produce(draft.user, userDraft => {
userDraft.name += "!"
})
})

Drafts 在引用上不相等#

Immer 中的 draft 对象包装在 Proxy 中,因此您不能使用 ===== 来测试原始对象与其 draft 之间的相等性(例如,当匹配数组中的特定元素时)。相反,您可以使用 original 助手:

const remove = produce((list, element) => {
const index = list.indexOf(element) // 不会工作!
const index = original(list).indexOf(element) // 用这个!
if (index > -1) list.splice(index, 1)
})
const values = [a, b, c]
remove(values, a)

如果可以的话,建议在 produce 函数之外执行比较,或者使用 .id 之类的唯一标识符属性,以避免需要使用 original