React--》掌握Valtio让状态管理变得轻松优雅
Valtio采用了代理模式,使状态管理变得更加直观和易于使用,同时能够与React等框架无缝集成,本文将深入探讨Valtio的核心概念、使用场景以及其在提升应用性能中的重要作用,帮助你掌握这一强大工具,从而提升开发效率和用户体验。
目录
初识Valtio
Valtio基础使用
代理与快照
订阅与侦听
历史与对象
初识Valtio
Valtio是一个轻量级的状态管理库,专为现代前端框架react设计,它使用JS的代理(Proxy)特性,提供了一种简单而高效的方式来管理和共享状态,其官方网址为:地址 ,如下所示:

Proxy概念:是一种用于拦截和定义基本操作(如属性查找、赋值、枚举、函数调用等)的对象,它可以在JS中通过Proxy构造函数创建。可以理解成在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。其优势如下:
1)可以监听整个对象而非属性及监听数组的变化
2)不改变语句语法,实现额外的操作或守卫功能。
3)返回的是一个新对象,可以只操作新的对象达到目的
4)增强了js的元编程能力,方便实现各种内部DSL。
使用Valtio理由:因为其允许开发者轻松地创建可响应的状态对象,使得状态变化能够自动触发UI更新,这使得开发者能够更方便地管理应用中的状态,同时保持代码的简洁和可读性,其具备的优势如下所示:
1)简单易用:Valtio 的 API 直观,学习曲线平缓,开发者可以快速上手。
2)高性能:通过使用代理,Valtio 只会对变化的部分进行更新,避免了不必要的重渲染,提高了性能。
3)无缝集成:与 React 和其他框架的集成非常顺畅,允许开发者在熟悉的环境中使用。
4)小巧轻便:Valtio 的体积小,依赖少,不会给项目带来额外负担。
5)可组合性:可以与其他状态管理工具(如 Zustand 或 Redux)结合使用,增强灵活性。
valtio使用:具有较少的api,核心就两个proxy与useProx,如下所示:
1)proxy:用于包装原始对象,生成可监听修改对象状态方法也就是可修改的状态state,组件中使用操作状态的函数来生成渲染用的snapshot来反映到原始对象上。
2)useProxy:用于返回每次渲染间的不可修改对象snapshot,相当于state的一份拷贝snapshot仅用于视图的展示。
state与snapshot都进行了Proxy处理,state的代理会修改原始对象,snapshot的代理则会用于记录引用类型使用情况,接下来我们通过代码进行一个演示,终端执行如下命令对Valtio进行一个安装:
npm i valtio
下面这段代码创建了一个简单的状态管理对象,允许通过addCount函数来更新count值,使用其代理功能可以确保当count变化时任何依赖于该值的 UI 组件都会自动更新,我们用proxy包裹一个状态,当然在其中也可以定义许多其他状态,如下所示:
import { proxy } from "valtio";type IStore = {count: number
}const store = proxy<IStore>({count: 1
})
export const addCount = (num: number) => {store.count += num
}
export default store
在根组件中我使用Valtio的useSnapshot解构快照,组件会在count改变时自动更新从而实现了简单的状态管理和UI交互,如下所示:
import { useSnapshot } from "valtio";
import store, { addCount } from "./store";const App = () => {const { count } = useSnapshot(store);return (<><h1>计数器 -- {count}</h1><button onClick={() => addCount(1)}>改变</button></>)
}export default App;
最终呈现的效果如下所示:

Valtio基础使用
由于Valtio采用了基于 Proxy 的设计理念使得状态管理变得轻量且高效,通过它开发者能够轻松创建响应式状态实时更新 UI,而无需复杂的配置或冗长的代码,接下来我们开始对其进行一个简单使用的介绍
代理与快照
proxy代理:当我们使用代理proxy时需要注意以下几个方面:
proxy会跟踪对原始对象和所有嵌套对象的更改,并在修改对象时通知侦听器:
import { proxy } from 'valtio'const state = proxy({ count: 0, text: 'hello' })
const personState = proxy({ name: 'Timo', role: 'admin' })
const authState = proxy({ status: 'loggedIn', user: personState }) // 嵌套代理// 可以像普通js一样对proxy对象中的属性进行操作
setInterval(() => {++state.count
}, 1000)
// 代理可以嵌套在其他proxy对象中并作为一个整体进行更新
authState.user.name = 'Nina'
proxy可以对代理的属性值进行异步操作,并可以接收多种格式数据的代理:
import { proxy } from 'valtio'// 异步代理
const bombState = proxy({explosion: new Promise((resolve) => setTimeout(() => resolve('Boom!'), 3000)),
})
// 代理对象可以包含任何类型的值,包括函数
const state = proxy({chart: d3.select('#chart'),component: React.createElement('div'),map: new Map(), // see proxyMapstorage: localStorage,
})
如果不想被proxy代理又想取值,可以使用ref进行包裹:
import { proxy, ref } from 'valtio'const state = proxy({count: 0,dom: ref(document.body),
})
useSnapshot快照:创建捕获更改的本地snapshot,将Valtio快照包装在访问跟踪代理中确保组件渲染优化,即只有当它(或其子组件)专门访问的键发生更改时它才会重新渲染,而不是在代理的每一次更改时:
在组件渲染中读取快照,在valtio回调中使用代理。快照是只读的,进行任何读取修改等操作都需要通过代理进行以便回调读取和写入最新值:

useSnapshot依赖于子代理的原始引用,所以如果用新的引用替换它,组件 订阅旧代理的不会收到新的更新,因为它仍然订阅旧代理,可以使用下面方法,不需要担心重新渲染因为它是渲染优化的:
const snap = useSnapshot(state)
return <div>{snap.profile.name}</div>const { profile } = useSnapshot(state)
return <div>{profile.name}</div>
订阅与侦听
subscribe订阅:可以访问组件外部的状态并订阅更改
Valtio中的subscribe函数,可以在任何组件中如果proxy中的对象发生变化都会被执行,作用是为整个代理对象添加订阅功能,当代理对象的任何部分发生变化时,注册的回调函数都会被触发,这使得开发者可以监控整个状态的变化,并在变化时执行特定的逻辑:


subscribeKey函数是为特定的状态键添加订阅功能,当该键的值发生变化时注册的回调函数会被触发,这使得开发者能够精确地响应状态变化从而实现更细粒度的更新和优化性能:
import { useEffect } from "react";
import { useSnapshot } from "valtio";
import { subscribeKey } from "valtio/utils";
import store, { addCount } from "./store";const App = () => {const { count } = useSnapshot(store);useEffect(() => {const unsubscribe = subscribeKey(store, 'count', (v) =>console.log('state has changed to', v),)return () => unsubscribe()}, [])return (<><h1>计数器 -- {count}</h1><button onClick={() => addCount(1)}>改变</button></>)
}export default App;
watch侦听:与只侦听单个代理的subscribe不同,watch支持订阅多个代理对象,对代理对象(或其子代理)的任何更改都将重新运行回调:
用于监控代理状态的变化并在状态发生变化时执行特定的回调函数,与subscribe不同watch主要用于观察特定的状态部分,可以更精确地响应变化:
import { useSnapshot } from "valtio";
import { watch } from 'valtio/utils'
import store, { addCount } from "./store";
import { useEffect } from "react";const App = () => {const { count } = useSnapshot(store);useEffect(() => {const stop = watch((get) => {console.log("state has changed to", get(store).count);})return () => stop();}, [])return (<><h1>计数器 -- {count}</h1><button onClick={() => addCount(1)}>改变</button></>)
}export default App;

历史与对象
proxyWithHistory历史:终端执行 npm i valtio-history 按照该插件,可以使用proxyWithHistory实用函数用于创建具有快照历史记录的代理。
创建一个具有历史记录功能的代理对象。这意味着你可以追踪状态的变化,记录每次修改的历史,并允许你在需要时恢复到之前的状态:
import "./App.css";
import { useSnapshot } from "valtio";
import { proxyWithHistory } from "valtio-history";
import React from "react";const textProxy = proxyWithHistory({text: "Add some text to this initial value and then undo/redo",
});
const update = (event: React.ChangeEvent<HTMLTextAreaElement>) =>(textProxy.value.text = event.target.value);export default function App() {const { value, undo, redo, history, canUndo, canRedo, getCurrentChangeDate } = useSnapshot(textProxy);return (<div className="App"><h2>Editor with history</h2><div className="info"><span>change {history.index + 1} / {history.nodes.length}</span><span>|</span><span>{getCurrentChangeDate().toISOString()}</span></div><div className="editor"><textarea value={value.text} rows={4} onChange={update} /></div><button onClick={undo} disabled={!canUndo()}>Undo</button><button onClick={redo} disabled={!canRedo()}>Redo</button></div>);
}

proxySet与proxyMap操作:两个函数用于创建特定类型的代理对象,以便更方便地管理集合数据结构:
proxySet:用于创建一个代理对象,表示一个集合(Set)。它允许你跟踪集合中的元素变化,比如添加、删除等。
import { proxySet } from 'valtio/utils'// 创建一个代理集合
const mySet = proxySet(new Set());
// 添加元素
mySet.add('item1');
mySet.add('item2');
// 监听集合变化
console.log([...mySet]); // 输出: ['item1', 'item2']
// 删除元素
mySet.delete('item1');
console.log([...mySet]); // 输出: ['item2']
proxyMap:用于创建一个代理对象表示一个映射(Map),它可以跟踪键值对的变化,例如添加、删除或更新键值对。
import { proxyMap } from 'valtio/utils';// 创建一个代理映射
const myMap = proxyMap(new Map());
// 添加键值对
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
// 监听映射变化
console.log([...myMap.entries()]); // 输出: [['key1', 'value1'], ['key2', 'value2']]
// 更新键值对
myMap.set('key1', 'updatedValue');
console.log(myMap.get('key1')); // 输出: 'updatedValue'
// 删除键值对
myMap.delete('key2');
console.log([...myMap.entries()]); // 输出: [['key1', 'updatedValue']]
总结:通过本文的探讨,我们可以看到Valtio在状态管理方面的强大与灵活性。它的代理机制使得响应式编程变得简单,其设计不仅提高了开发效率,还促进了代码的可维护性,无论是小型项目还是大型应用,Valtio都为前端开发者提供了一个高效而直观的解决方案,是现代状态管理的理想选择。
相关文章:
React--》掌握Valtio让状态管理变得轻松优雅
Valtio采用了代理模式,使状态管理变得更加直观和易于使用,同时能够与React等框架无缝集成,本文将深入探讨Valtio的核心概念、使用场景以及其在提升应用性能中的重要作用,帮助你掌握这一强大工具,从而提升开发效率和用户…...
python爬虫百度图片
直接给代码,可直接用,个人需要修改的地方有两处: self.directory 这是本地存储地址,修改为自己电脑的地址,另外,**{}**不要删spider.json_count 10 这是下载的图像组数,一组有30张图像&#x…...
前端开发:Vue中数据绑定原理
Vue 中最大的一个特征就是数据的双向绑定,而这种双向绑定的形式,一方面表现在元数据与衍生数据之间的响应,另一方面表现在元数据与视图之间的响应,而这些响应的实现方式,依赖的是数据链,因此,要…...
CTF-RE 从0到N: TEA
TEA TEA(Tiny Encryption Algorithm,轻量加密算法) 是一种简单、快速的对称加密算法。它是一个分组加密算法,通常用于加密 64 位的数据块,并使用 128 位的密钥。TEA 是一种“费斯妥结构”(Feistel structu…...
python 使用PIL获取图片长宽
在Python中,你可以使用Pillow库(PIL的一个分支和替代品)来获取图片的长和宽。Pillow提供了丰富的图像处理功能,包括获取图像的基本属性,如尺寸。 以下是一个简单的示例,展示了如何使用Pillow库来获取图片的…...
【Nas】X-DOC:搞机之PVE部署All In One(黑群晖NAS 软路由OpenWrt Docker Win10远程桌面)
【Nas】X-DOC:搞机之PVE部署All In One(黑群晖NAS & 软路由OpenWrt & Docker & Win10远程桌面) 1、原硬件配置清单:2、改AIO后增加配置清单:3、虚拟化平台PVE:4、搭建的关键服务: 1…...
linux 驱动源码分析的理解。
首先 , 是linux 驱动,我看网上的老师,在分析源码时 , 不会 所有的函数都分析,而是分析一些比较重要的函数,一些厉害的人,在分析源码时…...
鸿蒙-任务栏右击退出 或 UIAbility窗口关闭,怎么弹框拦截
onPrepareToTerminate 需要配置权限 ohos.permission.PREPARE_APP_TERMINATE 参考链接:文档中心import { emitter } from kit.BasicServicesKit; import { common } from kit.AbilityKit; import { TipsDialog } from kit.ArkUI;// entryAbility.ets 在你的uiabilit…...
【C++进阶篇】——STL的简介
【C进阶篇】——STL的简介 1.什么是STL STL(standard template libaray-标准模板库):是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…...
信息安全工程师(70)网络攻击陷阱技术与应用
前言 网络攻击陷阱技术是一种主动的防御方法,作为网络安全的重要策略和技术手段,有利于网络安全管理者获得信息优势。 一、网络攻击陷阱技术原理 网络攻击陷阱技术可以消耗攻击者所拥有的资源,加重攻击者的工作量,迷惑攻击者&…...
Web保存状态的手段(Session的使用)
一,JSP中的page指令 1. <% page language“java” session“true”%> session:此页面是否使用session,默认值为true 二,使用Session完善之前的登录程序 1. 如何禁止直接输入URL地址进入登录功能的欢迎界面? …...
第五十四章 安全元素的详细信息 - DerivedKeyToken 详情
文章目录 第五十四章 安全元素的详细信息 - <DerivedKeyToken> 详情详情消息中的位置 第五十四章 安全元素的详细信息 - 详情 <DerivedKeyToken> 的目的是携带发送者和接收者可以独立使用的信息来生成相同的对称密钥。这些方可以使用该对称密钥对 SOAP 消息的相关…...
kafka 的高可用机制是什么?
大家好,我是锋哥。今天分享关于【kafka 的高可用机制是什么?】面试题?希望对大家有帮助; kafka 的高可用机制是什么? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个分布式消息系统&am…...
4.1.3 网站通信技术
文章目录 1. 网站通信方式2. URL - 统一资源定位符定义格式演示 3. 发送请求的4种形式在地址栏中输入URL访问超链接href属性指定URLform表单在action中指定URL通过AJAX请求后端数据 4. 两种不同返回的请求发送URL,后端处理完响应页面发送AJAX请求,后端处…...
Java-图书管理系统
我的个人主页 欢迎来到我的Java图书管理系统,接下来让我们一同探索如何书写图书管理系统吧! 1管理端和用户端 2建立相关的三个包(book、operation、user) 3建立程序入口Main类 4程序运行 1.首先图书馆管理系统分为管理员端和…...
python如何通过json以及pickle读写保存数据
记录信息 比如说我写了这样一段程序,记录了爱吃的食物: food_list []while True:c input("输入1添加新的食物,输入2查询已添加的食物,输入exit退出:")if c "1":new_food input("输入你…...
【SPIE出版,EI检索稳定】2024年人机交互与虚拟现实国际会议(HCIVR 2024,11月15-17日)
2024年人机交互与虚拟现实国际会议(HCIVR 2024) 2024 International Conference on Human-Computer Interaction and Virtual Reality 官方信息 会议官网:www.hcivr.org 2024 International Conference on Human-Computer Interaction and …...
Linux vim编辑器
前言: 首先我们来了解一下什么是编辑器,通常我们在widow系统下例如C/C我们进行写代码时,我们通过vs2022等等编译器进行,这里的编译器是一种IDE(集成开发环境),集成开发环境是将代码编辑器、编译…...
普推知产:申请商标名称从4字改成3字下了初审!
近日7月的时候普推知产老杨帮客户申请的水果猕猴桃31类商标,初步审定公告下来了,基本没什么问题三个月公告结束后一个月内就可以拿到商标注册证,客户所在地全国有名猕猴桃之县,同质化竞争还得需要商标才可以。 刚开始了解到这位做…...
Flink 状态精准一次性特性
Flink 的一个重大价值在于, 它既保证了 exactly-once ,也具有低延迟和高吞吐 的处理能力 。 1.端到端(End-To-End)状态一致性 端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每 一…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
