跳到主要内容

修改层级很深的内部状态 ?

object 层级过深,厌倦了手动进行状态不可变更新(immutably)来修改内部状态?

如果你有一个层级很深的 object ,就像这样:

type State = {
deep: {
nested: {
obj: { count: number },
...{} // more
}
}
}

更新该嵌套状态需要费一些力,以确保该状态修改过程为不可变更新(immutably)。

正常操作

ReactRedux 类似,正常的方法是复制状态对象的每一层。这是通过扩展运算符完成的...,并手动将其合并到新的状态值中。像这样:

  normalInc: () =>
set((state) => ({
deep: {
...state.deep,
nested: {
...state.deep.nested,
obj: {
...state.deep.nested.obj,
count: state.deep.nested.obj.count + 1
}
}
}
})),

这太繁琐了!让我们来探索一些可以让你的编码生活更美好,且少掉一些头发的方法。

immer

许多人使用 Immer 来更新嵌套值。Immer 可以随时使用,你需要更新嵌套状态,如在React, Redux,当然,还有 Zustand!

可以使用 Immer 缩短嵌套深度对象的状态更新。让我们来看一个例子:

import produce from 'immer'

const useStore = create((set) => ({
deep: {
nested: {
obj: { count: 0 },
}
},
immerInc: () =>
set(
produce((state: State) => {
++state.deep.nested.obj.count
})
),
}))

const immerInc = useStore((state) => state.immerInc)
immerInc()

多简单啊!是不是保住了你的头发,请注意这里列出的陷阱

optics-ts

另一种方式是用 optics-ts

import * as O from 'optics-ts'

const useStore = create((set) => ({
deep: {
nested: {
obj: { count: 0 },
}
},
opticsInc: () =>
set(
O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)
),
}))

const opticsInc = useStore((state) => state.opticsInc)
opticsInc()

Ramda

还有 Ramda

import * as O from 'optics-ts'

const useStore = create((set) => ({
deep: {
nested: {
obj: { count: 0 },
}
},
ramdaInc: () =>
set(
R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)
),
}))

const ramdaInc = useStore((state) => state.ramdaInc)
ramdaInc()

Code Demo