实战指南:使用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来简化复杂状态更新有了深入的了解。

相关推荐
齐 飞6 分钟前
MongoDB笔记02-MongoDB基本常用命令
前端·数据库·笔记·后端·mongodb
巧克力小猫猿22 分钟前
基于ant组件库挑选框组件-封装滚动刷新的分页挑选框
前端·javascript·vue.js
FinGet31 分钟前
那总结下来,react就是落后了
前端·react.js
前端李易安31 分钟前
手写一个axios方法
前端·vue.js·axios
XinZong1 小时前
【VSCode插件推荐】想准时下班,你需要codemoss的帮助,分享AI写代码的愉快体验,附详细安装教程
前端·程序员
trim1 小时前
写了个可以在工作中快速摄取知识的神器,都来体验体验
前端·产品
ErvinHowell1 小时前
文件MD5生成性能大提升!如何实现分片与Worker优化
前端·vue.js·算法
想做白天梦1 小时前
LeetCode :150. 逆波兰表达式求值(含求后缀表达式和中缀转后缀表达式)
java·前端·算法
s甜甜的学习之旅2 小时前
前端js处理list(数组)
开发语言·前端·javascript
小布布的不2 小时前
MyBatis 返回 Map 或 List<Map>时,时间类型数据,默认为LocalDateTime,响应给前端默认含有‘T‘字符
前端·mybatis·springboot