实战指南:使用Zustand与Immer简化状态管理

本文是《React管理平台》第八节

通过本文我们将学会如何在zustand中使用Immer来简化复杂状态更新。

快速安装zustand

首先,我们需要通过以下命令安装zustand依赖:

shell 复制代码
# npm
npm i zustand

# pnpm
pnpm i zustand

# yarn

yarn add zustand

安装成功后,将在项目中看到如下依赖提示:

创建store目录和相关文件

store目录下,创建user.tsindex.ts文件。

index.ts的代码只需将useUserStoreuser.ts中导出即可:

tsx 复制代码
export { useUserStore } from './user'

设定用户状态存储 - user.ts

user.ts文件中,我们构建用户状态管理的核心代码:

tsx 复制代码
// 引入zustand创建方法
import { create } from 'zustand'

// 用户信息类型定义
type UserInfo = {
  username: string
  avatar: string
}

// 动作类型定义
type Action = {
  updateToken: (token: string) => void
  updateUserInfo: (userInfo: UserInfo) => void
}

// 状态接口定义
interface State {
  token: string
  userInfo: UserInfo
}

// 创建zustand存储
export const useUserStore = create<State & Action>(set => ({
  token: '',
  userInfo: { username: '', avatar: '' },
  updateToken: token => set({ token: token }),
  updateUserInfo: userInfo => set({ userInfo: userInfo })
}))

详解user.ts核心功能

步骤一:导入 create 方法

首先,我们从zustand库中导入了create 方法。

tsx 复制代码
import { create } from 'zustand'

步骤二:定义状态和动作类型

接着,我们定义了两种类型:UserInfoAction

  • UserInfo 类型用于描述用户的基本信息。包含 usernameavatar 两个字符串。
  • Action 类型有 updateTokenupdateUserInfo 两个更新函数。
tsx 复制代码
type UserInfo = {
  username: string
  avatar: string
}

type Action = {
  updateToken: (token: string) => void
  updateUserInfo: (userInfo: UserInfo) => void
}

步骤三:设定状态接口

之后,我们定义了 State 的接口,以此表示存储中将会有哪些状态。

  • State 接口中有 tokenuserInfo 两个属性,分别用于存储用户的认证令牌和用户信息。
tsx 复制代码
interface State {
  token: string
  userInfo: UserInfo
}

步骤四:创建 Zustand 存储

使用 create 方法和 Zustand 存储函数来创建存储。

  • create 方法接收一个设置函数(setter),该函数定义了存储的初始化状态以及如何更新这些状态。
  • 存储的初始状态中,token 是空字符串,userInfo 也是包含空字符串的对象。
tsx 复制代码
export const useUserStore = create<State & Action>(set => ({
  token: '',
  userInfo: { username: '', avatar: '' },
  // ...
}))

步骤五:实现更新动作

在此存储函数中,我们定义了两个状态更新函数 updateTokenupdateUserInfo

  • updateToken 函数接受 token 参数,并使用 set 方法更新存储中的 token 值。
  • updateUserInfo 函数接受 userInfo 对象作为参数,并同样使用 set 来更新存储中的 userInfo
tsx 复制代码
export const useUserStore = create<State & Action>(set => ({
  // ...
  updateToken: token =>
    set({
      token: token
    }),
  updateUserInfo: userInfo =>
    set({
      userInfo: userInfo
    })
}))

步骤六:使用 useUserStore 钩子 最后,我们将创建的存储导出为 useUserStore 钩子。这样,在组件中就可以通过调用这个钩子来访问和更新状态了。

zustand 中使用 Immer

首先,在项目中安装immer依赖:

shell 复制代码
# npm
npm i immer

# pnpm
pnpm i immer

# yarn

yarn add immer

安装完成后,依赖包会出现在项目中,如下图所示:

使用Immer中间件优化zustand存储

使用Immer中间件,使我们的状态更新代码更加直观和易于管理。

我们只需要使用 immer 包裹住之前的设置函数(setter)即可,以下是集成Immer后的zustand代码:

tsx 复制代码
// 引入zustand库和Immer中间件
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'


type UserInfo = {
  username: string
  avatar: string
}

type Action = {
  updateToken: (token: string) => void
  updateUserInfo?: (userInfo: UserInfo) => void
  updateUserName: (username: string) => void
}

interface State {
  token: string
  userInfo: UserInfo
}

// 创建带有Immer中间件的zustand存储
export const useUserStore = create<State & Action>()(

  // 这里使用了immer进行包裹住"设置函数"(setter)
  immer(set => ({
    token: '',
    userInfo: { username: '', avatar: 'http://xxxx.com/yy.jpg' },
    updateToken: token => set(state => { state.token = token }),
    updateUserName: username => set(state => { state.userInfo.username = username })
  }))
)

Immer的工作原理及对比

使用Immer可以愉快地更新不可变状态(immutable state)------我们不再需要使用...展开运算符手动复制对象。Immer提供了简化的API来更新深层嵌套的数据。

例如,通过Immer进行状态更新时:

tsx 复制代码
updateUserName: username => set(state => { state.userInfo.username = username })

而传统的不使用Immer的代码需要使用拓展运算符来保留所有既有状态,并手动指定更改:

tsx 复制代码
updateUserName: username =>
set(state => ({
  ...state, // 再次使用展开运算符复制所有既有状态
  userInfo: {
    ...state.userInfo, // 复制 userInfo 对象内的其他属性
    username: username // 只更新 username 属性
  }
}))

效果展示

如下动图展示了使用Immer更新状态的响应效果:

我们来对比不使用 immer 的代码,会发现很难维护:

tsx 复制代码
export const useUserStore = create<State & Action>(set => ({
  token: '',
  userInfo: { username: '', avatar: 'http://xxxx.com/yy.jpg' },
  updateToken: token =>
    set(state => ({
      ...state, // 使用展开运算符复制所有既有状态
      token: token // 更新 token 值
    })),
  updateUserName: username =>
    set(state => ({
      ...state, // 再次使用展开运算符复制所有既有状态
      userInfo: {
        ...state.userInfo, // 复制 userInfo 对象内的其他属性
        username: username // 只更新 username 属性
      }
    }))
}))

总结

通过本文,我们对如何在zustand中使用Immer来简化复杂状态更新有了深入的了解。

相关推荐
无限大.20 分钟前
前端知识速记:节流与防抖
前端
十八朵郁金香22 分钟前
【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能
前端·javascript·vue.js
学问小小谢26 分钟前
第26节课:内容安全策略(CSP)—构建安全网页的防御盾
运维·服务器·前端·网络·学习·安全
LCG元1 小时前
Vue.js组件开发-实现全屏图片文字缩放切换特效
前端·javascript·vue.js
还是鼠鼠2 小时前
图书管理系统 Axios 源码__新增图书
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
还是鼠鼠5 小时前
图书管理系统 Axios 源码 __删除图书功能
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
轻口味5 小时前
Vue.js `Suspense` 和异步组件加载
前端·javascript·vue.js
m0_zj6 小时前
8.[前端开发-CSS]Day08-图形-字体-字体图标-元素定位
前端·css
还是鼠鼠7 小时前
图书管理系统 Axios 源码__编辑图书
前端·javascript·vscode·ajax·前端框架
北极象7 小时前
vue3中el-input无法获得焦点的问题
前端·javascript·vue.js