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

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采用了代理模式&#xff0c;使状态管理变得更加直观和易于使用&#xff0c;同时能够与React等框架无缝集成&#xff0c;本文将深入探讨Valtio的核心概念、使用场景以及其在提升应用性能中的重要作用&#xff0c;帮助你掌握这一强大工具&#xff0c;从而提升开发效率和用户…...

python爬虫百度图片

直接给代码&#xff0c;可直接用&#xff0c;个人需要修改的地方有两处&#xff1a; self.directory 这是本地存储地址&#xff0c;修改为自己电脑的地址&#xff0c;另外&#xff0c;**{}**不要删spider.json_count 10 这是下载的图像组数&#xff0c;一组有30张图像&#x…...

前端开发:Vue中数据绑定原理

Vue 中最大的一个特征就是数据的双向绑定&#xff0c;而这种双向绑定的形式&#xff0c;一方面表现在元数据与衍生数据之间的响应&#xff0c;另一方面表现在元数据与视图之间的响应&#xff0c;而这些响应的实现方式&#xff0c;依赖的是数据链&#xff0c;因此&#xff0c;要…...

CTF-RE 从0到N: TEA

TEA TEA&#xff08;Tiny Encryption Algorithm&#xff0c;轻量加密算法&#xff09; 是一种简单、快速的对称加密算法。它是一个分组加密算法&#xff0c;通常用于加密 64 位的数据块&#xff0c;并使用 128 位的密钥。TEA 是一种“费斯妥结构”&#xff08;Feistel structu…...

python 使用PIL获取图片长宽

在Python中&#xff0c;你可以使用Pillow库&#xff08;PIL的一个分支和替代品&#xff09;来获取图片的长和宽。Pillow提供了丰富的图像处理功能&#xff0c;包括获取图像的基本属性&#xff0c;如尺寸。 以下是一个简单的示例&#xff0c;展示了如何使用Pillow库来获取图片的…...

【Nas】X-DOC:搞机之PVE部署All In One(黑群晖NAS 软路由OpenWrt Docker Win10远程桌面)

【Nas】X-DOC&#xff1a;搞机之PVE部署All In One&#xff08;黑群晖NAS & 软路由OpenWrt & Docker & Win10远程桌面&#xff09; 1、原硬件配置清单&#xff1a;2、改AIO后增加配置清单&#xff1a;3、虚拟化平台PVE&#xff1a;4、搭建的关键服务&#xff1a; 1…...

linux 驱动源码分析的理解。

首先 &#xff0c; 是&#xff4c;&#xff49;&#xff4e;&#xff55;&#xff58; 驱动&#xff0c;我看网上的老师&#xff0c;在分析源码时 &#xff0c; 不会 所有的函数都分析&#xff0c;而是分析一些比较重要的函数&#xff0c;一些厉害的人&#xff0c;在分析源码时…...

鸿蒙-任务栏右击退出 或 UIAbility窗口关闭,怎么弹框拦截

onPrepareToTerminate 需要配置权限 ohos.permission.PREPARE_APP_TERMINATE 参考链接&#xff1a;文档中心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-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…...

信息安全工程师(70)网络攻击陷阱技术与应用

前言 网络攻击陷阱技术是一种主动的防御方法&#xff0c;作为网络安全的重要策略和技术手段&#xff0c;有利于网络安全管理者获得信息优势。 一、网络攻击陷阱技术原理 网络攻击陷阱技术可以消耗攻击者所拥有的资源&#xff0c;加重攻击者的工作量&#xff0c;迷惑攻击者&…...

Web保存状态的手段(Session的使用)

一&#xff0c;JSP中的page指令 1. <% page language“java” session“true”%> session&#xff1a;此页面是否使用session&#xff0c;默认值为true 二&#xff0c;使用Session完善之前的登录程序 1. 如何禁止直接输入URL地址进入登录功能的欢迎界面&#xff1f; …...

第五十四章 安全元素的详细信息 - DerivedKeyToken 详情

文章目录 第五十四章 安全元素的详细信息 - <DerivedKeyToken> 详情详情消息中的位置 第五十四章 安全元素的详细信息 - 详情 <DerivedKeyToken> 的目的是携带发送者和接收者可以独立使用的信息来生成相同的对称密钥。这些方可以使用该对称密钥对 SOAP 消息的相关…...

kafka 的高可用机制是什么?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 的高可用机制是什么&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 的高可用机制是什么&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个分布式消息系统&am…...

4.1.3 网站通信技术

文章目录 1. 网站通信方式2. URL - 统一资源定位符定义格式演示 3. 发送请求的4种形式在地址栏中输入URL访问超链接href属性指定URLform表单在action中指定URL通过AJAX请求后端数据 4. 两种不同返回的请求发送URL&#xff0c;后端处理完响应页面发送AJAX请求&#xff0c;后端处…...

Java-图书管理系统

我的个人主页 欢迎来到我的Java图书管理系统&#xff0c;接下来让我们一同探索如何书写图书管理系统吧&#xff01; 1管理端和用户端 2建立相关的三个包&#xff08;book、operation、user&#xff09; 3建立程序入口Main类 4程序运行 1.首先图书馆管理系统分为管理员端和…...

python如何通过json以及pickle读写保存数据

记录信息 比如说我写了这样一段程序&#xff0c;记录了爱吃的食物&#xff1a; food_list []while True:c input("输入1添加新的食物&#xff0c;输入2查询已添加的食物&#xff0c;输入exit退出&#xff1a;")if c "1":new_food input("输入你…...

【SPIE出版,EI检索稳定】2024年人机交互与虚拟现实国际会议(HCIVR 2024,11月15-17日)

2024年人机交互与虚拟现实国际会议&#xff08;HCIVR 2024&#xff09; 2024 International Conference on Human-Computer Interaction and Virtual Reality 官方信息 会议官网&#xff1a;www.hcivr.org 2024 International Conference on Human-Computer Interaction and …...

Linux vim编辑器

前言&#xff1a; 首先我们来了解一下什么是编辑器&#xff0c;通常我们在widow系统下例如C/C我们进行写代码时&#xff0c;我们通过vs2022等等编译器进行&#xff0c;这里的编译器是一种IDE&#xff08;集成开发环境&#xff09;&#xff0c;集成开发环境是将代码编辑器、编译…...

普推知产:申请商标名称从4字改成3字下了初审!

近日7月的时候普推知产老杨帮客户申请的水果猕猴桃31类商标&#xff0c;初步审定公告下来了&#xff0c;基本没什么问题三个月公告结束后一个月内就可以拿到商标注册证&#xff0c;客户所在地全国有名猕猴桃之县&#xff0c;同质化竞争还得需要商标才可以。 刚开始了解到这位做…...

Flink 状态精准一次性特性

Flink 的一个重大价值在于&#xff0c; 它既保证了 exactly-once &#xff0c;也具有低延迟和高吞吐 的处理能力 。 1.端到端&#xff08;End-To-End&#xff09;状态一致性 端到端的一致性保证&#xff0c;意味着结果的正确性贯穿了整个流处理应用的始终&#xff1b;每 一…...

算法笔记day08

1.游游的水果大礼包 这道题很容易让人想到贪心&#xff0c;但是其实是一道枚举题&#xff0c;只需举一个反例&#xff0c;贪心无非就是哪个礼包价值大&#xff0c;就优先组成哪个礼包。 算法思路&#xff1a; 设可以组成x个a礼包&#xff0c;y个b礼包&#xff0c;求ax by的最…...

在Selenium中有哪些元素对象操作方法?( ̄﹃ ̄)

在Selenium中&#xff0c;driver.find_element() 方法用于定位页面上的单个元素。一旦你定位到了一个元素&#xff0c;你可以对这个元素执行多种操作。此外&#xff0c;driver.find_element() 只是定位元素的方法之一&#xff0c;它还有其他一些相关的方法和属性&#xff0c;但…...

sqli-labs靶场安装以及刷题记录-docker

sqli-labs靶场安装以及刷题记录-docker sqli-labs靶场安装-dockersqli-labs靶场刷题less-1 单引号less-2 数字型less-3 单引号括号less-4 双引号括号less-5 单引号布尔盲注less-6 双引号布尔盲注less-7 单引号加括号、输出到文件less-8 单引号布尔盲注less-9 单引号时间盲注les…...

谷歌仓库管理工具repo

Google 的仓库管理工具 Repo 是一个用于管理包含多个 Git 仓库的大型项目的命令行工具。它最初由 Google 为 Android 操作系统开发&#xff0c;以简化和协调众多子项目的版本控制和协作过程。以下是对 Repo 的详细解释&#xff1a; 主要功能与特点 多仓库管理&#xff1a; Rep…...

C#的自定义Tip窗体 - 开源研究系列文章

上次编写了自定义的提示和对话框窗体&#xff0c;这次记录的是自定义的Tip窗体&#xff0c;用于显示提示操作。有时间没编程了&#xff0c;这次就当进行了记录。 1、 项目目录&#xff1b; 2、 源码介绍&#xff1b; 1) 实现&#xff1b; 2) 应用&#xff1b; 3、 运行界面&…...

目前最新 Reflector V11.1.0.2067版本 .NET 反编译软件

目前最新 Reflector V11.1.0.2067版本 .NET 反编译软件 一、简介二、.NET Reflector的主要功能包括&#xff1a;1. **反编译**: 反编译是将已编译的.NET程序集&#xff08;如.dll或.exe文件&#xff09;转换回可读的源代码。这使得开发者可以查看和学习第三方库的实现细节&…...

软考:CORBA架构

CORBA过时了吗 CORBA指南 个人小结&#xff1a; IPC&#xff0c;进程间通信&#xff0c;Socket应用在不同机器之间的通信 RPC是一种技术思想而非一种规范 但站在八九十年代的当口&#xff0c;简单来说&#xff0c;就是我在本地调用了一个函数&#xff0c;或者对象的方法&…...

qt QSaveFile详解

QSaveFile 是 Qt 提供的一个类&#xff0c;用于安全地保存文件。它的主要特点是在写入文件时确保数据完整性&#xff0c;以防止文件损坏。使用 QSaveFile&#xff0c;您可以创建一个临时文件&#xff0c;并在成功写入后将其重命名为目标文件&#xff0c;这样可以避免在写入过程…...

【Linux】线程池详解及其基本架构与单例模式实现

目录 1.关于线程池的基本理论 1.1.线程池是什么&#xff1f; 1.2.线程池的应用场景&#xff1a; 2.线程池的基本架构 2.1.线程容器 2.2.任务队列 2.3.线程函数&#xff08;HandlerTask&#xff09; 2.4.线程唤醒机制 3.添加单例模式 3.1.单例模式是什么&…...

运输层知识点汇总3

目录 前言 二、为什么在TCP首部中有一个首部长度字段&#xff0c;而UDP的首部就没有这个字段&#xff1f; 三、一个TCP报文段的数据部分最多为多少字节&#xff1f;为什么&#xff1f;如果用户要传送的数据的字节长度超过TCP报文段中的序号字段可能编出的最大序号&#xff0…...

php工具箱是直接做网站的吗/郴州网站seo外包

综合实验 实验要求&#xff1a; 1、添加vlan10&#xff0c;vlan20&#xff0c;vlan30&#xff0c;vlan40&#xff0c;SW1与SW2之间做以太网通道&#xff0c;与交换机SW3、4、5、6之间做trunk链路。 2、vpcs1分配vlan10&#xff0c;真机分配vlan20&#xff0c;vpcs2分配vlan30&a…...

wordpress更换百度编辑器/站长平台

关键词&#xff1a;学习力、生产力、笔记本软件、surface使用指南引入&#xff1a;在信息化迅速发展的今天&#xff0c;智能手机、计算机已经成为人们必不可少的工具。要想专注于办公学习&#xff0c;一个好的载体必不可少&#xff0c;所以本文集中讲一下我个人对于笔记本电脑或…...

怎么做教育类型的网站/seo优化培训班

一、“平面设计”的内涵 • 所谓“平面设计”&#xff0c;指的是在平面空间上的设计活动&#xff0c;其设计的内容主要是在二维空间中各个元素的设计和这些元素组合的布局设计&#xff0c;其中包括字体设计、版面设计、插图、摄影的采用&#xff0c;而所有这些内容的核心在于传…...

网络销售应该注册什么公司/电商seo是指

// func3-2.cpp algo3-6.cpp和algo3-7.cpp要调用的函数 char Precede(SElemType t1,SElemType t2) { // 根据教科书表3.1&#xff0c;判断t1&#xff0c;t2两符号的优先关系(#用\n代替)char f;switch(t2){case :case -:if(t1(||t1\n)f<; // t1<t2elsef>; // t1>t2b…...

家具网站php源码/个人免费推广网站

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#includemain(){int acount0,bcount0,ccount0,dcount0;char a;printf("请输入一行字符:\n");a getchar();while (a !\n){switch (a){caseq:casew:casee:caser:caset:casey:caseu:casei:caseo:casep:casea:cases:cased:c…...

学做网站的书籍/百度云资源链接分享群组

时间&#xff1a;2014.04.29 地点&#xff1a;基地二楼 ---------------------------------------------------------------------------------------------- 一、题目 定义字符串的左旋转操作&#xff1a;把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转…...