当前位置: 首页 > ai >正文

Zustand 第二章(状态处理)

状态处理

在上一章我们讲了,Zustand会合并第一层的state,但是如果深层次应该如何处理呢

来吧演示

首先创建一个葫芦娃,葫芦娃有七个娃,每个娃都有自己的状态,我们可以通过updateGourd来更新葫芦娃的状态,这样就实现了一个深层次的demo

import { create } from 'zustand'interface User {gourd: {oneChild: string,twoChild: string,threeChild: string,fourChild: string,fiveChild: string,sixChild: string,sevenChild: string,},updateGourd: () => void
}
const useUserStore = create<User>(((set) => ({//创建葫芦娃gourd: {oneChild: '大娃',twoChild: '二娃',threeChild: '三娃',fourChild: '四娃',fiveChild: '五娃',sixChild: '六娃',sevenChild: '七娃',},updateGourd: () => set((state) => ({gourd: {//...state.gourd, 先不进行状态合并  // [!code highlight] oneChild: '大娃-超进化',}}))
})))export default useUserStore;

在这里插入图片描述

我们会发现如果不进行状态合并,其他的状态是会丢失的,所以深层次的状态处理需要进行状态合并,但是如果代码过多,每次都需要合并状态也挺烦的,所以我们可以通过immer中间件处理这个问题

使用immer中间件

安装

npm install immer

原始immer的用法

需要导出produce,然后它的第一个参数是原始值,第二个参数是一个回调函数,回调函数中的参数是draft,也就是原始值的拷贝,然后我们就可以直接修改draft了,最后返回新的值

import { produce } from 'immer'const data = {user: {name: '张三',age: 18}
}const newData = produce(data, draft => {draft.user.age = 20
})console.log(newData,data) 
//{ user: { name: '张三', age: 20 } } 
//{ user: { name: '张三', age: 18 } }

immerZustand中的使用方法

引入注意是从zustand/middleware/immer引入,而不是immer

import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
  1. 首先从zustand中间件引入immer
  2. 然后注意结构create()(immer())这里是两个括号而不是放在create里面了
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
interface User {gourd: {oneChild: string,twoChild: string,threeChild: string,fourChild: string,fiveChild: string,sixChild: string,sevenChild: string,},updateGourd: () => void
}
//注意结构发生了变化!!!
const useUserStore = create<User>()(immer(((set) => ({//创建葫芦娃gourd: {oneChild: '大娃',twoChild: '二娃',threeChild: '三娃',fourChild: '四娃',fiveChild: '五娃',sixChild: '六娃',sevenChild: '七娃',},updateGourd: () => set((state) => {state.gourd.oneChild = '大娃-超进化' //这儿不需要状态合并了需要修改什么值直接改就行了state.gourd.twoChild = '二娃-谁来了'state.gourd.threeChild = '三娃-我来了'})
}))))export default useUserStore;

immer原理剖析

immer.js 通过 Proxy 代理对象的所有操作,实现不可变数据的更新。当对数据进行修改时,immer 会创建一个被修改对象的副本,并在副本上进行修改,最后返回修改后的新对象,而原始对象保持不变。这种机制确保了数据的不可变性,同时提供了直观的修改方式。

immer 的核心原理基于以下两个概念:

  1. 写时复制 (Copy-on-Write)
  • 无修改时:直接返回原对象
  • 有修改时:创建新对象
  1. 惰性代理 (Lazy Proxy)
  • 按需创建代理
  • 通过 Proxy 拦截操作
  • 延迟代理创建

工作流程

调用 produce
创建代理 draft
执行 recipe 修改 draft
是否有修改!
创建新对象...base + ...modified
直接返回 base

简化实现

type Draft<T> = {-readonly [P in keyof T]: T[P];
};function produce<T>(base: T, recipe: (draft: Draft<T>) => void): T {// 用于存储修改过的对象const modified: Record<string, any> = {};const handler = {get(target: any, prop: string) {// 如果这个对象已经被修改过,返回修改后的对象if (prop in modified) {return modified[prop];}// 如果访问的是对象,则递归创建代理if (typeof target[prop] === 'object' && target[prop] !== null) {return new Proxy(target[prop], handler);}return target[prop];},set(target: any, prop: string, value: any) {// 记录修改modified[prop] = value;return true;}};// 创建代理对象const proxy = new Proxy(base, handler);// 执行修改函数recipe(proxy);// 如果没有修改,直接返回原对象if (Object.keys(modified).length === 0) {return base;}// 创建新对象,只复制修改过的属性return JSON.parse(JSON.stringify(proxy))
}// 使用示例
const state = {user: {name: '张三',age: 25}
};const newState = produce(state, draft => {draft.user.name = '李四';draft.user.age = 26;
});console.log(state);     // { user: { name: '张三', age: 25 } }
console.log(newState);  // { user: { name: '李四', age: 26 } }

这儿只是简单实现,没有考虑数组的情况和深层次的代理,只实现了其核心思想

http://www.xdnf.cn/news/12739.html

相关文章:

  • 【设计模式】2.策略模式
  • 【网站建设】不同类型网站如何选择服务器?建站项目实战总结
  • AI智能体|扣子(Coze)搭建【公众号对标文章采集拆解】工作流
  • DeepSeek11-Ollama + Open WebUI 搭建本地 RAG 知识库全流程指南
  • windows10下搭建nfs服务器
  • 【分布式】分布式ID介绍和实现方案总结
  • 力扣算法题1
  • Vue部署到Nginx上及问题解决
  • 深入理解 React Hooks
  • 通过css实现正方体效果
  • 【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
  • C++11 右值引用
  • Pandas-如何正确将两张数据表进行合并
  • 自定义protoc-gen-go生成Go结构体,统一字段命名与JSON标签风格
  • 【Zephyr 系列 15】构建企业级 BLE 模块通用框架:驱动 + 事件 + 状态机 + 低功耗全栈设计
  • github开源协议选择
  • iview-admin静态资源js按需加载配置
  • STM标准库-TIM旋转编码器
  • JAVASCRIPT 前端数据库-V6--仙盟数据库架构-—-—仙盟创梦IDE
  • 深入浅出 Arrays.sort(DualPivotQuicksort):如何结合快排、归并、堆排序和插入排序
  • 2025年夏第九届河北工业大学程序设计校赛
  • Linux 上的 Tomcat 端口占用排查
  • 2025-06-08 思考-人被基因和社会关系双重制约
  • Psychopy音频的使用
  • 实验四:图像灰度处理
  • 自动化立体仓库堆垛机控制系统STEP7 OB1功能块
  • python打卡day48
  • 《解锁树莓派+Java:TinyML模型部署的性能飞升秘籍》
  • Java 面向对象进阶之多态:从概念到实践的深度解析
  • Windmill:开源开发者基础设施的革命者