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

相关文章

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.