zustand 搞定 react 中复杂状态管理

2023-12-23 15:36

本文主要是介绍zustand 搞定 react 中复杂状态管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Zustand 是一个轻量级的、无依赖的状态库,适用于 React 和函数式编程。它提供了一个简单、灵活的方式来管理应用程序的状态。本文就讲讲如何使用 zustand 搞定 react 中复杂状态管理,进而替代 redux

React + Zustand 状态管理

一、前言

redux 为代表的这类单向数据流状态管理库,都是需要在最外层(根组件)包一个 Provider , Context 中的值都在 Provider 的作用域下有效,这样才能做到数据状态共享。
Zustand 则另辟蹊径,默认不需要 Provider,就想 Vue 中 pinia 状态管理库一样,直接声明一个 hooks 式的 useStore 后就可以在不同组件中进行调用,并且保持它们的状态共享和响应式更新。

Zustand 在德语中是 state 状态的意思

二、Zustand 基本使用

  1. 定义 Store 数据
// src/store/user.jsimport { create } from 'zustand'const initData = {userInfo: {},token: '',
}export const useUserStore = create((set, get) => ({...initData,setUserInfo: (userInfo) => set({ userInfo }),getUsername: () => {return get().userInfo?.username}
}))
  1. 在组件中使用
import {useUserStore} from '@/store/user.js'
import axios from "axios";const Component = () => {const {token, setUserInfo, getUsername} = useUserStore()const userInfo = useUserStore((state)=>state.userInfo)const fetchUser = async () => {let state = useUserStore.getState()const { data } = await axios({url: '/xxx',headers: {'access-token': state.token,}})setUserInfo(data)}return (<div>用户:{getUsername()}</div>)
}export default Component

注意:

  1. 在 react hook 组件中函数体内部使用全局的 state,需要使用 getState() 方法获取,否则获取的是初始化的 state 值。
  2. zustand 的 state 是响应式的,所以可以直接在 jsx ui 中使用解构的 state 值 ,但是在非 jsx 中需要使用 getState() 方法获取最新状态。

三、Zustand 进阶用法

适用于跨组件数据共享、数据监听操作。

数据监听需要使用 subscribeWithSelector 包裹,否则不能细粒度监听。

const unsub1 = useDogStore.subscribe(console.log)
  1. 定义 Store 数据
// src/store/dialog.jsimport { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'const initData = {newDialogVisible: false,newFormData: null,
}export const useDialogStore = create(subscribeWithSelector((set, get) => ({...initData,changeNewDialog(visible, data = null) {set({ newDialogVisible: visible, newFormData: data })},}))
)
  1. 设置数据
import { forwardRef, useImperativeHandle, useState } from 'react'
import { Button, Form } from 'antd'
import { useDialogStore } from '@/store/dialog.js'const Dialog = (props, ref) => {useImperativeHandle(ref, () => ({showModal,}))const [form] = Form.useForm()const { changeNewDialog } = useDialogStore()const showModal = (data) => {changeNewDialog(true, {})}return (<><Button onClick={showModal} htmlType="submit">新建</Button></>)
}export default forwardRef(Dialog)
  1. 监听数据变化
import { Breadcrumb } from 'antd'
import Side from './components/Side.jsx'
import List from './components/List.jsx'
import NewDialog from './components/NewDialog.jsx'
import { useEffect, useRef } from 'react'
import { useDialogStore } from '@/store/dialog.js'
import { shallow } from 'zustand/shallow'const Page = () => {const newDialogRef = useRef()useEffect(() => {// 监听数据变化const unsub = useDialogStore.subscribe((state) => [state.newDialogVisible, state.newFormData],([visible, data]) => {if (visible) {// console.log(visible, data)newDialogRef.current.showModal(data)}},{ equalityFn: shallow } // 浅比较)return () => {// 取消订阅unsub()}}, [])return (<><Breadcrumbitems={[{title: '首页',},{title: <a href="/">列表</a>,},]}/><div className="border-b-[1px] border-solid border-gray-300 ml-[-20px] mr-[-20px] mt-[15px]"></div><div className="flex justify-between"><Side /><List /></div><NewDialog ref={newDialogRef} /></>)
}export default Page
  1. 其他用法
import { subscribeWithSelector } from 'zustand/middleware'
const useDogStore = create(subscribeWithSelector(() => ({ paw: true, snout: true, fur: true }))
)// Listening to selected changes, in this case when "paw" changes
const unsub2 = useDogStore.subscribe((state) => state.paw, console.log)
// Subscribe also exposes the previous value
const unsub3 = useDogStore.subscribe((state) => state.paw,(paw, previousPaw) => console.log(paw, previousPaw)
)
// Subscribe also supports an optional equality function
const unsub4 = useDogStore.subscribe((state) => [state.paw, state.fur],console.log,{ equalityFn: shallow }
)
// Subscribe and fire immediately
const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {fireImmediately: true,
})

四、在 React 组件外使用

在 axios 或路由守卫中通常需要获取/设置全局的 token 和用户信息,使用 zustand 可以这样做:

  1. 获取状态
// react 组件外直接取值
const token = useUserStore.getState().token
  1. 设置更新状态
// react 组件外更新值
useUserStore.setState({ userInfo: data })

参考文档:

  • https://www.npmjs.com/package/zustand
  • https://mp.weixin.qq.com/s/bqPJWzWWBk_dnKUBq0btPg
  • https://zhuanlan.zhihu.com/p/591981209
  • https://www.jianshu.com/p/516c85c50da8

欢迎访问:天问博客

这篇关于zustand 搞定 react 中复杂状态管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/528538

相关文章

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

Spring Boot Actuator应用监控与管理的详细步骤

《SpringBootActuator应用监控与管理的详细步骤》SpringBootActuator是SpringBoot的监控工具,提供健康检查、性能指标、日志管理等核心功能,支持自定义和扩展端... 目录一、 Spring Boot Actuator 概述二、 集成 Spring Boot Actuat

MySQL多实例管理如何在一台主机上运行多个mysql

《MySQL多实例管理如何在一台主机上运行多个mysql》文章详解了在Linux主机上通过二进制方式安装MySQL多实例的步骤,涵盖端口配置、数据目录准备、初始化与启动流程,以及排错方法,适用于构建读... 目录一、什么是mysql多实例二、二进制方式安装MySQL1.获取二进制代码包2.安装基础依赖3.清

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动