使用 Zustand 和 Immer 管理 React 应用的状态
让我们开始编码
我相信每个人在处理嵌套数据时都会感到如此饱和,以至于他们想知道是否有更简单的解决方案。
我没有详细介绍 Immer.js 是什么或 JavaScript 不变性是什么,因为互联网上有很多免费的精彩文章可以比我更好地解释它。
如果您对上一段提到的观点感兴趣,我建议您阅读这篇文章。
在这个例子中,我将使用 Inner.js 和 Zustand,但您也可以将它与许多其他状态管理器一起使用。我相信,使用 Inner.js 之后,您将不再需要使用任何其他工具来处理状态中的对象和数组。
今天我将用不同的方式编写示例代码。这次我将提供大部分代码(GitHub 仓库在这里),并且我将重点介绍我们的商店,因为今天重要的是将 Zustand 与 Immer 结合使用。
让我们开始编码
这个项目的目的是列出我们喜欢或知道的韩剧名称。应用程序中需要的功能以及我们必须实现的功能是添加、删除和编辑。
我们的商店的初始代码如下:
// @src/store.js
import create from "zustand";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
// Mutations will go here
}));
您可能已经注意到,初始状态已经有 2 个元素,如果您在端口上运行该应用程序,3000
您应该会得到类似这样的视觉结果:
让我们从实现添加新的戏剧突变开始,让我们创建一个名为的动作addDrama()
,它将接收有效载荷作为单个参数。
然后我们将导入 immer 并使用 produce 函数,这样我们就可以复制当前状态,以便对其进行相应的变异,如下所示:
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
// Logic goes here
});
})
),
// More mutations will go here
}));
现在,我们不再直接将新剧情添加到状态中,而是在草稿中进行添加。这种方法与原生 JavaScript 非常相似,我们只需使用该.push()
方法即可。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
// More mutations will go here
}));
现在你应该可以将一部新的韩剧添加到列表中了。方法如下:

现在我们要创建一个新的突变,但这次我们要消除列表中的一个“drama”。我们将我们的动作命名为removeDrama()
。它的唯一参数也是有效载荷。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
// Logic goes here
})
),
// More mutations will go here
}));
首先,让我们在数组中寻找具有与我们的有效载荷相等的键的元素的索引,在本例中是 id。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
const dramaIndex = draft.kdramas.findIndex((el) => el.id === payload);
// More logic goes here
})
),
// More mutations will go here
}));
然后只需从数组中删除具有其索引的元素即可。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
const dramaIndex = draft.kdramas.findIndex((el) => el.id === payload);
draft.kdramas.splice(dramaIndex, 1);
})
),
// More mutations will go here
}));
通过这种方式,我们已经成功消除了列表中存在的元素,如下所示:

现在我们只需要实现并更新列表中的一部韩剧。为此,让我们创建一个名为 的新动作patchDrama()
。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
const dramaIndex = draft.kdramas.findIndex((el) => el.id === payload);
draft.kdramas.splice(dramaIndex, 1);
})
),
patchDrama: (payload) =>
set(
produce((draft) => {
// Logic goes here
})
),
}));
首先让我们尝试通过其 id 找到数组元素。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
const dramaIndex = draft.kdramas.findIndex((el) => el.id === payload);
draft.kdramas.splice(dramaIndex, 1);
})
),
patchDrama: (payload) =>
set(
produce((draft) => {
const drama = draft.kdramas.find((el) => el.id === payload.id);
// More logic goes here
})
),
}));
现在我们只需要用有效载荷的值来更新元素的名称属性。
// @src/store.js
import create from "zustand";
import produce from "immer";
export const useStore = create((set) => ({
kdramas: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
addDrama: (payload) =>
set(
produce((draft) => {
draft.kdramas.push({
id: Math.floor(Math.random() * 100),
name: payload,
});
})
),
removeDrama: (payload) =>
set(
produce((draft) => {
const dramaIndex = draft.kdramas.findIndex((el) => el.id === payload);
draft.kdramas.splice(dramaIndex, 1);
})
),
patchDrama: (payload) =>
set(
produce((draft) => {
const drama = draft.kdramas.find((el) => el.id === payload.id);
drama.name = payload.name;
})
),
}));
这样我们就可以更新列表的元素,如下所示:

结论
您可能已经注意到,当使用 immer 时,处理我们状态下的对象和数组使得过程变得更加简单,而不必担心扩展操作。
与往常一样,我希望这篇文章能够对您有所帮助,即使它提供的信息较少,而且更多的是实际代码。
祝你度过美好的一天!👋 ☺️
文章来源:https://dev.to/franciscomendes10866/zustand-and-immer-with-react-5ajh