react.js 手写响应式 reactive
Redux 太繁琐,Mbox 很酷但我们可能没必要引入新的包,那就让我们亲自在 react.js 中通过代理实现一套钩子来达到类似 vue 的响应式状态:
实现 reactive hooks
代理类声明
代理状态的类应当提供可访问的状态,和订阅变化的接口。
export type Listener<T> = (state: T) => any;export interface ReactiveCtxModel<T = any> {value: T;subscribe(listener: Listener<T>): () => void;
}
代理类实现
使用 es6 class 来实现代理类,es6 class 提供了属性的 get/set 访问器,让我们在通过 obj.key 的方式访问时不是直接访问,而是经过了代理,真实的值则通过 private 被设置为私有属性。
类的构造器,我们传入用 React.useState 获得的返回,没错,想在 react 中让页面响应数据的变化,我们仍然需要 useState,不传 setState 的话,这个 Reactive 将是惰性的,因为他无法触发页面的重渲染。
私有属性除了要保存的 state,还有 listeners 数组来保存监听变化要触发的函数,这些函数在 state 每次被 set 访问器调用时跟着调用。
export class Reactive<T = any> implements ReactiveCtxModel {private _state: T;private _setState: any = (newState: T) => {this._state = newState;};private _listeners: Listener<Readonly<T>>[] = [];constructor(state: T, setState?: any) {this._state = state;setState ? (this._setState = setState) : void 0;}get value(): T {return this._state;}set value(newState: T) {this._setState?.(newState);this._listeners.forEach((listener) => listener(newState));}subscribe(listener: Listener<T>) {this._listeners.push(listener);return () => {this._listeners = this._listeners.filter((l) => l !== listener);};}static isReactive(obj: any) {return Reactive.prototype.isPrototypeOf(obj);}
}
实现创建代理的钩子函数
每次在代码里手动创建 useState() 然后还要 new Reactive() 太麻烦了,我们将这几个操作封装成一个 hook Reactify,然后再赋给 reactive,这样我们就可以直接使用 reactive(initialValue) 创建响应式对象。(为什么要先创建 Reactify?因为 react 约定 react 的 use 钩子的顶部空间应当命名为 useXXX 或者是 大写字母 开头,因为我喜欢 reactive 这个名字,所以做了一个交换)
const Reactify = <T = any>(initialValue: T): Reactive<T> => {const [state, setState] = React.useState<T>(initialValue);const observer = new Reactive(state, setState);return observer;
};
/*** reactive is same with Reactify*/
export const reactive = Reactify;
example:
const Demo: React.FC = () => {let state = reactive(0);const num = state.value;return (<><ButtononClick={() => {state.value = state.value + 1;}}>{num}</Button></>);
};
实现监听函数
直接在 Reactive 对象上调用 subscribe 很棒,但有时候我更喜欢这个操作可以抽出来,于是有了下面这个 listen 函数,传入要监听的 Reactive 对象,接着在 then 中链式传入要触发的回调,观感上更优雅。
/*** When store.state changes, call the given function.* @param target listened Reactive store* @returns unlistener*/
export function listen<T = any>(target: Omit<Reactive<T>, "_state" | "_setState">) {return {then: (...fns: ((value: T) => any)[]) => {const fn = (value: T) => fns.forEach((f) => f(value));const dispose = target.subscribe(fn);return dispose;},};
}
example:
listen(obj).then((newVal) => {console.log(`newVal: ${newVal}`);});
借助 Context 传递 Reactive
以上的 reactive 只能在单组件局部使用,即使通过 props 传递给子组件,子组件也只有只读的权利。如果需要跨组件共享 Reactive 代理,我们可以借助 React.Context:
创建默认 Context
import { createContext } from "react";
import { Listener, Reactive } from "./model";export const createReactiveContext = <T = any>(initialValue?: T) => {const reactiveObject = new Reactive(initialValue);return createContext<ReactiveCtxModel<T> | undefined>(reactiveObject as any);
};const ReactiveCtx = createReactiveContext();export default ReactiveCtx;
实现 useReactive 钩子
useReactive 可以接收一个初值,如果得到了初值就开辟一个新的 context 和 Reactive 对象,否则延用上一步创建的 ReactiveCtx。
/*** Accept a value and return a reactive object. When initalValue is valid a new reactive object will be created.*/
export const useReactive = <T = any>(initialValue?: T): Reactive<T> => {const [state, setState] = React.useState<T>(initialValue ?? (undefined as T));const reactiveObj = new Reactive(state, setState);const defaultContextModel = React.useContext((initialValue as any) ?? ReactiveCtx);if (initialValue !== undefined && initialValue !== null) {return reactiveObj as Reactive<T>;}return defaultContextModel as Reactive<T>;
};
实现 useReactiveContext 钩子
useReactive 接收初值后新建的 context 不能为其它组件获取,要让其它组件共享非默认的 context,我们就需要在外部额外创建并导出新的 context,并实现一个 useReactiveContext 钩子来接收新的context,这样就可以共享新的 context,同样如果没有传入新的 context,我们将沿用默认的 ReactiveCtx。
export const useReativeContext = <T = any>(context?: React.Context<ReactiveCtxModel<T> | undefined>): Reactive<T> => {const reactiveCtxModel = React.useContext(context || ReactiveCtx);return reactiveCtxModel as Reactive<T>;
};
现在,我们将原先 demo 中使用的 raective 替换为 useReactive,然后我们即可自由的跨组件共享 Reactive。
example:
const Demo: React.FC = () => {let state = useReactive(0);const num = state.value;listen(state).then((newVal) => {console(`newVal: ${newVal}`);});return (<><Button$click={() => {state.value = state.value + 1;}}>{num}</Button><ReactiveCtx.Provider value={state}><Kid /></ReactiveCtx.Provider></>);
};
Kid:
function Kid() {const state = useReactive<number>();return (<><Taglightstyle={{ cursor: "pointer" }}onClick={() => {state.value++;}}>state : {state.value}</Tag><Taglightstyle={{ cursor: "pointer" }}onClick={() => {state2.value++;}}>state2 : {state2.value}</Tag><context.Provider value={state2}><KidKid /></context.Provider></>);
}
Bingo! 到这里我们就基本实现了 reactive 啦,拜拜~
相关文章:
react.js 手写响应式 reactive
Redux 太繁琐,Mbox 很酷但我们可能没必要引入新的包,那就让我们亲自在 react.js 中通过代理实现一套钩子来达到类似 vue 的响应式状态: 实现 reactive hooks 代理类声明 代理状态的类应当提供可访问的状态,和订阅变化的接口。 …...
代码随想录打卡第四十六天|完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ
完全背包理论 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。 完全背包和01背包问题唯一…...
【BP-Adaboost预测】基于BP神经网络的Adaboost的单维时间序列预测研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Origami Studio for Mac:塑造未来,掌握原型设计之巅
在当今高度竞争的设计领域,原型设计的重要性不言而喻。它不仅是沟通想法,也是测试和改进设计的关键环节。而现在,一款强大的原型设计工具——Origami Studio for Mac,正在席卷设计界,以其独特的功能和卓越的性能&#…...
UML类图中各箭头表示总结
UML类图中各箭头表示总结 1、泛化2、实现3、依赖4、关联5、聚合6、组合 在UML类图中,箭头关系是用来表示类之间的关系的。箭头关系的种类有以下几种: 1、泛化 泛化:表示类之间的继承关系。箭头从子类指向父类。箭头:实线空心三角…...
神经网络量化----为了部署而特别设计
引言:一般神经网络量化有两个目的: 为了加速,在某些平台上浮点数计算比较耗费时间,替换为整形可以加快运算为了部署,某些平台上只支持整形运算,比如在芯片中如果是第1个目的,则使用常规的量化手段就可以满足,将浮点数运算变成整形运算+较少的浮点运算 但是如果是第2个目…...
代码随想录算法训练营Day60|单调栈01
代码随想录算法训练营Day60|单调栈01 文章目录 代码随想录算法训练营Day60|单调栈01一、739. 每日温度二、496.下一个更大元素 I 一、739. 每日温度 class Solution {public int[] dailyTemperatures(int[] temperatures) {//单调栈int lenstemperatures.length;int result[]n…...
openMP学习笔记 -编程模型
OpenMP模型 gcc编译openmp指令:gcc test.cpp -o test -fopenmp 定积分计算 函数面积 给定一个定积分,计算其面积: ∫ 0 1 4.0 ( 1 x 2 ) d x \int^{1}_{0}{\frac{4.0}{(1x^2)}dx} ∫01(1x2)4.0dx omp 概念 并行区域 并行区域用于…...
【Hive SQL 每日一题】环比增长率、环比增长率、复合增长率
文章目录 环比增长率同比增长率复合增长率测试数据需求说明需求实现 环比增长率 环比增长率是指两个相邻时段之间某种指标的增长率。通常来说,环比增长率是比较两个连续时间段内某项数据的增长量大小的百分比。 环比增长率反映了两个相邻时间段内某种经济指标的变…...
Java设计模式之外观模式(Facade Pattern)
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。外观模式通过隐藏子系统的复杂性,简化了客户端与子系统之间的交互,提供了一个更简单、更直观的接口。…...
【大疆智图】大疆智图(DJI Terra 3.0.0)安装及使用教程
大疆智图是一款以二维正射影像与三维模型重建为主的软件,同时提供二维多光谱重建、激光雷达点云处理、精细化巡检等功能。它能够将无人机采集的数据可视化,实时生成高精度、高质量三维模型,满足事故现场、工程监测、电力巡线等场景的展示与精确测量需求。 文章目录 1. 安装D…...
腾讯地图基本使用(撒点位,点位点击,弹框等...功能) 搭配Vue3
腾讯地图的基础注册账号 展示地图等基础功能在专栏的上一篇内容 大家有兴趣可以去看一看 今天说的是腾讯地图的在稍微一点的基础操作 话不多说 直接上代码 var marker ref(null) var map var center ref(null) // 地图初始化 const initMap () > {//定义地图中心点坐标…...
散列表:Word文档中的单词拼写检查功能是如何实现的?
文章来源于极客时间前google工程师−王争专栏。 一旦我们在Word里输入一个错误的英文单词,它就会用标红的方式提示“编写错误”。Word的这个单词拼写检查功能,虽然很小但却非常实用。这个功能是如何实现的? 散列别(Hash Table&am…...
智慧公厕蜕变多功能城市智慧驿站公厕的创新
随着城市发展的不断推进,对公共设施的便利性和智能化要求也日益提高。为满足市民对高品质、便捷、舒适的公共厕所的需求,智慧公厕行业的领航厂家广州中期科技有限公司,全新推出了一体化智慧公厕驿站。凭借着“高科技碳中和物联网创意设计新经…...
R语言清洗与处理数据常用代码段
去掉数据框df的某一列: # 删除不必要的变量 data$unnecessary_var <- NULL 选择需要的列进行读入数据框: # 选择需要的列 selected_cols <- c("col1", "col2", "col3") data <- fread("data.csv", s…...
centos 7.9 安装python 3.10的tls问题,
本地开发升级成了py3.10.6,服务器测试时安装py3.10.4 发现无法正常使用pip3 pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. 印象中py3的高版本依赖高版本的openssl,centos 7下默认的openssl为1.0.x, 问题很简…...
pytorch,tf维度理解RNN
input_t input_t.squeeze(1) 这行代码用于从 input_t 中去除尺寸为1的维度。在深度学习中,经常会出现具有额外尺寸为1的维度,这些维度通常是为了匹配模型的期望输入维度而添加的。 在这里,input_t可能具有形状 (batch_size, 1, feature_dim…...
算法刷题-数组
算法刷题 209. 长度最小的子数组-二分或者滑动窗口 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数…...
可视化数学分析软件 MATLAB R2021b mac中文版软件介绍
MATLAB R2021b mac作为数学类科技应用软件中首屈一指的商业数学软件,可以帮助您进行矩阵运算、绘制函数和数据、实现算法、创建用户界面、连接其他编程语言的程序等,主要应用于工程计算、控制设计、信号处理与通讯、图像处理、信号检测、金融建模设计与分析等领域。…...
罗技摄像头左右翻转
需要下载驱动lws(我的是c310) LWS 罗技摄像头驱动下载 打开驱动程序,高级设置。有个镜像。...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
