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实现前端灰度发布

《Nginx实现前端灰度发布》灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新,通过灰度发布,我们可以测试新版本的稳定性和性能,下面就来介绍一下前端灰度发布的使用,感... 目录前言一、基于权重的流量分配二、基于 Cookie 的分流三、基于请求头的分流四、基于请求参数的分

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

HTML5 data-*自定义数据属性的示例代码

《HTML5data-*自定义数据属性的示例代码》HTML5的自定义数据属性(data-*)提供了一种标准化的方法在HTML元素上存储额外信息,可以通过JavaScript访问、修改和在CSS中使用... 目录引言基本概念使用自定义数据属性1. 在 html 中定义2. 通过 JavaScript 访问3.

CSS模拟 html 的 title 属性(鼠标悬浮显示提示文字效果)

《CSS模拟html的title属性(鼠标悬浮显示提示文字效果)》:本文主要介绍了如何使用CSS模拟HTML的title属性,通过鼠标悬浮显示提示文字效果,通过设置`.tipBox`和`.tipBox.tipContent`的样式,实现了提示内容的隐藏和显示,详细内容请阅读本文,希望能对你有所帮助... 效

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构

Flutter监听当前页面可见与隐藏状态的代码详解

《Flutter监听当前页面可见与隐藏状态的代码详解》文章介绍了如何在Flutter中使用路由观察者来监听应用进入前台或后台状态以及页面的显示和隐藏,并通过代码示例讲解的非常详细,需要的朋友可以参考下... flutter 可以监听 app 进入前台还是后台状态,也可以监听当http://www.cppcn

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

SpringBoot实现导出复杂对象到Excel文件

《SpringBoot实现导出复杂对象到Excel文件》这篇文章主要为大家详细介绍了如何使用Hutool和EasyExcel两种方式来实现在SpringBoot项目中导出复杂对象到Excel文件,需要... 在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计