陷阱
性能提示
对于性能提示,阅读 性能提示.
不要重新分配 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
。