使用 Zustand 和 Immer 管理 React 应用的状态 让我们开始编码

2025-06-05

使用 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
}));
Enter fullscreen mode Exit fullscreen mode

您可能已经注意到,初始状态已经有 2 个元素,如果您在端口上运行该应用程序,3000您应该会得到类似这样的视觉结果:

Web 应用程序

让我们从实现添加新的戏剧突变开始,让我们创建一个名为的动作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
}));
Enter fullscreen mode Exit fullscreen mode

现在,我们不再直接将新剧情添加到状态中,而是在草稿中进行添加。这种方法与原生 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
}));
Enter fullscreen mode Exit fullscreen mode

现在你应该可以将一部新的韩剧添加到列表中了。方法如下:

添加新剧

现在我们要创建一个新的突变,但这次我们要消除列表中的一个“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
}));
Enter fullscreen mode Exit fullscreen mode

首先,让我们在数组中寻找具有与我们的有效载荷相等的键的元素的索引,在本例中是 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
}));
Enter fullscreen mode Exit fullscreen mode

然后只需从数组中删除具有其索引的元素即可。

// @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
}));
Enter fullscreen mode Exit fullscreen mode

通过这种方式,我们已经成功消除了列表中存在的元素,如下所示:

删除一部韩剧

现在我们只需要实现并更新列表中的一部韩剧。为此,让我们创建一个名为 的新动作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
      })
    ),
}));
Enter fullscreen mode Exit fullscreen mode

首先让我们尝试通过其 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
      })
    ),
}));
Enter fullscreen mode Exit fullscreen mode

现在我们只需要用有效载荷的值来更新元素的名称属性。

// @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;
      })
    ),
}));
Enter fullscreen mode Exit fullscreen mode

这样我们就可以更新列表的元素,如下所示:

更新韩剧

结论

您可能已经注意到,当使用 immer 时,处理我们状态下的对象和数组使得过程变得更加简单,而不必担心扩展操作。

与往常一样,我希望这篇文章能够对您有所帮助,即使它提供的信息较少,而且更多的是实际代码。

祝你度过美好的一天!👋 ☺️

文章来源:https://dev.to/franciscomendes10866/zustand-and-immer-with-react-5ajh
PREV
我如何在我的网站上实现黑暗模式
NEXT
在 Node.js 中使用 JWT 和 Cookies