react js自定义实现状态管理
redux基础实现
myRedux
export const createStore = (reduce) => {if (typeof reduce !== 'function') throw new Error('Expected the reducer to be a function.')let state,listeners = []state = reduce()const getState = () => stateconst dispatch = (action) => {if(typeof action !== 'object' || typeof action.type !== 'string') throw new Error('Actions must be plain objects.')state = reduce(state, action)listeners.forEach(listener => listener())}const subscribe = (listener) => {if(typeof listener !== 'function') throw new Error('Expected the listener to be a function.')listeners.push(listener)return () => listeners = listeners.filter(l => l !== listener)}return {getState,dispatch,subscribe,}
}
使用
import React, { useEffect, useState } from 'react'
import { createStore } from './myRedux'const reduce = (state = { a: 123 }, action = {}) => {state = { ...state }switch (action.type) {case 'tset':state.a = Math.random() * 1000return statedefault:return state}}
const store = createStore(reduce)export default function Test() {const state = store.getState()const [_, foceUpdate] = useState(0)useEffect(() => {store.subscribe(() => {foceUpdate(Date.now())})}, [])const change = () => {store.dispatch({ type: 'tset' })}return (<div><h1>Test {state.a}</h1><button onClick={change} >change</button></div>)
}
react-redux
和源码可能不同,我没看过源码,只是实现一下
react-redux.js
import { useContext, useEffect, useState, createContext } from 'react'
const StoreContext = createContext()
export const Provider = (props) => {const store = props.storereturn <StoreContext.Provider value={{ store }}>{props.children}</StoreContext.Provider>
}export const connect = (mapState, mapDispatch) => {if (typeof mapState !== 'function') throw new Error('mapState must be an function')if (typeof mapDispatch !== 'function') throw new Error('mapDispatch must be an function')return (Cpn) => {return (props = {}) => {const contents = useContext(StoreContext)const store = contents.storeconst state = mapState(store.getState())const dispatch = mapDispatch(store.dispatch)const [_, forceUpdate] = useState(true)useEffect(() => {store.subscribe(() => {forceUpdate(Symbol())})}, [])props = { ...props, ...state, ...dispatch }return <Cpn {...props} />}}
}
使用
import React from 'react'
import { Provider, connect } from './react-redux'
import { createStore } from 'redux'
const reducer = (state = { name: 'test' }, action) => {switch (action.type) {case 'CHANGE_NAME':return { ...state, name: action.name }default:return state}
}
const store = createStore(reducer)function Test2(props) {const change = () => {props.changeName('test' + Math.random())}return (<div><h1>Test {props.name} </h1><button onClick={change} >change</button></div>)
}
const Test3 = connect(state => ({ name: state.name }),dispatch => ({ changeName: (name) => dispatch({ type: "CHANGE_NAME", name }) })
)(Test2)export default function Test() {return (<Provider store={store} ><Test3 /></Provider>)
}
模仿pinia方式管理
myPinia.js
import { useEffect, useState } from 'react'class StoreState {constructor(value) {this.value = valuethis.symbol = Symbol()}
}export const createStore = (f) => {if (typeof f !== 'function') throw new Error('Expected a function')const store = f()watch(store)const useStore = () => {return new Proxy(store, {get: (target, prop) => {const v = target[prop]const isState = v instanceof StoreStatereturn isState ? v.value : v},set: () => store,})}return useStore
}export const useStoreState = (v) => {return new StoreState(v)
}const watch = (obj) => {Object.keys(obj).forEach((key) => {const storeState = obj[key]if (storeState instanceof StoreState) {let value = storeState.valueObject.defineProperty(storeState, 'value', {get: () => value,set: (newValue) => {value = newValueupdateView()},})}})
}let listeners = []
export const subscribe = (f) => {if (typeof f !== 'function') throw new Error('Expected a function')if (!listeners.includes(f)) listeners.push(f)return () => (listeners = listeners.filter((l) => l !== f))
}
const updateView = () => listeners.forEach((f) => f())export const connect = (Cpn) => {return (props) => {const [_, forceUpdate] = useState(true)useEffect(() => {const unSubscribe = subscribe(() => forceUpdate(Symbol()))return unSubscribe}, [])return <Cpn {...props} />}
}
使用
import React from 'react'
import { createStore, useStoreState, connect } from './myPinia'const useUserStore = createStore(() => {let name = useStoreState('test')const change = () => {name.value = 'test2' + Math.random()}return { name, change }
})function Test() {const store = useUserStore()const change = () => {store.change()}return (<div><h2>Test {store.name}</h2><button onClick={change}>change</button></div>)
}export default connect(Test)
不足的是,还是需要forceUpdate
react-pinia
实现模块化
react-pinia.js
import { useEffect, useState } from 'react'
const storePool = {}
export const createStore = (id, f) => {if (typeof f !== 'function') throw new Error('Expected a function')const store = f()watchStore(store)const proxy = new Proxy(store, {get: (target, prop) => {const v = target[prop]const isState = v instanceof StoreStatereturn isState ? v.value : v},set: () => store,})storePool[id] = { store }const useStore = () => proxyreturn useStore
}class StoreState {constructor(value) {this.value = valuethis.symbol = Symbol()}
}
export const useStoreState = (v) => {return new StoreState(v)
}const watchStore = (obj) => {Object.keys(obj).forEach((key) => {const storeState = obj[key]if (storeState instanceof StoreState) {let value = storeState.valueconst symbol = storeState.symbolObject.defineProperty(storeState, 'value', {get: () => {return value},set: (newValue) => {value = newValueupdateView(symbol)},})}})
}let listenersPool = {}
export const subscribe = (symbol, f) => {if (typeof symbol !== 'symbol') throw new Error('Expected a symbol')if (typeof f !== 'function') throw new Error('Expected a function')const listeners = listenersPool[symbol] || []if (!listeners.includes(f)) listeners.push(f)listenersPool[symbol] = listenersreturn () => (listenersPool[symbol] = listenersPool[symbol].filter((l) => l !== f))
}
const updateView = (symbol) => {if (typeof symbol !== 'symbol') returnconst listeners = listenersPool[symbol] || []listeners.forEach((f) => f())
}export const connect = (map) => {const symbolArr = []const stores = (() => {const obj = {}Object.keys(storePool).forEach((key) => {const store = storePool[key].storeconst proxy = new Proxy(store, {get: (target, prop) => {const v = target[prop]const isState = v instanceof StoreStateif (isState) {const symbol = v.symbolif (!symbolArr.includes(symbol)) symbolArr.push(symbol)return v.value}return v},set: () => store,})obj[key] = proxy})return obj})()const mapStore = map(stores) || {}const mapStoreToProps = Object.prototype.toString.call(mapStore) === '[object Object]' ? mapStore : {}return (Cpn) => {return (props) => {const [, forceUpdate] = useState(true)useEffect(() => {const unSubscribe = symbolArr.map((id) => subscribe(id, () => forceUpdate(Symbol())))return () => unSubscribe.forEach((f) => f())}, [])return <Cpn {...props} {...mapStoreToProps} />}}
}
使用
import React, { useState } from 'react'
import { createStore, useStoreState, connect } from './react-pinia'// user store
const useUserStore = createStore('user', () => {let name = useStoreState('userStore')const change = () => {name.value = 'userStore' + Math.random()}return { name, change }
})
// test store
const useTestStore = createStore('test', () => {const test = useStoreState('testStore')const changeTest = () => {test.value = 'testStore' + Math.random()}return { test, changeTest }
}
)
const Test1 = () => {console.log('render Test1');const [showTest2, setShowTest2] = useState(true)return (<div><h1>test1</h1><button onClick={() => setShowTest2(false)} >hideTest2</button>{showTest2 && <Test2 />}<Test3 /></div>)
}const Test2 = connect(store => ({ name: store.user.name }))((props) => {console.log('render Test2', props);const store = useUserStore()const change = () => {store.change()}return (<div><h2>Test2 {store.name}</h2><button onClick={change}>change</button></div>)
}
)const Test3 = connect(store => [store.test.test])(() => {console.log('render Test3');const store = useTestStore()const change = () => {store.changeTest()}return (<div><h2>Test3 {store.test}</h2><button onClick={change}>change</button></div>)
}
)
export default Test1
相关文章:
react js自定义实现状态管理
redux基础实现 myRedux export const createStore (reduce) > {if (typeof reduce ! function) throw new Error(Expected the reducer to be a function.)let state,listeners []state reduce()const getState () > stateconst dispatch (action) > {if(typeo…...
行为型设计模式——中介者模式
中介者模式 中介者模式主要是将关联关系由一个中介者类统一管理维护,一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即…...
通信行业无线基本概念
fast roaming(快速漫游):使用户在不同的基站(access point)间可以平滑的切换,在802.11r协议标准中定义。band steering(波段转向):在双频段(2.4G和5G…...
grep 在运维中的常用可选项
一、对比两个文件 vim -d <filename1> <filename2> 演示: 需求:~目录下有两个文件一个test.txt 以及 text2.txt,需求对比两个文件的内容。 执行后会显示如图,不同会高亮。 二、两次过滤 场景:当需要多…...
python读取Dicom文件
文章目录 1. pydicom Library2. SimpleITK Library3. ITK Library (Insight Toolkit)4. GDCM Library (Grassroots DICOM) 下面提供几种用python方法读取Dicom文件 1. pydicom Library import pydicom # Read DICOM file dataset pydicom.dcmread("path_to_dicom_file.d…...
UL2034详细介绍UL 安全单站和多站一氧化碳报警器标准
在介绍相关标准之前先介绍一下UL认证和UL测试报告的区别,检测认证行业6年老司机 UL认证是自愿性的认证,需要检测产品和审核工厂,每个季度审核一次,费用高、时间久,而且审厂非常的严格。 UL测试报告是根据产品选用相应…...
鸿蒙HarmonyOS-SDK管理使用指南
鸿蒙HarmonyOS-SDK管理使用指南 文章目录 鸿蒙HarmonyOS-SDK管理使用指南sdkmgr使用指导查看SDK组件安装组件卸载组件查看sdkmgr版本查看sdkmgr帮助options选项说明ohsdkmgr使用指导查看SDK组件安装组件卸载组件查看ohsdkmgr版本查看ohsdkmgr帮助option...
QT上位机开发(进度条操作)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 进度条是一个比较常见的控件。如果某个操作需要很长的时间才能完成,那么这个时候最好有一个进度条提示,这样比较容易平复一…...
637_二叉树的层平均值
描述 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受 思路 无需思路,乱杀 解答 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, righ…...
Ubuntu20.4 Mono C# gtk 编程习练笔记(三)
Mono对gtk做了很努力的封装,即便如此仍然与System.Windows.Form中的控件操作方法有许多差异,这是gtk本身特性或称为特色决定的。下面是gtk常用控件在Mono C#中的一些用法。 Button控件 在工具箱中该控件的clicked信号双击后自动生成回调函数prototype&…...
What is `JsonSanitizer.sanitize` does?
JsonSanitizer.sanitize 是一个Java库中的方法,用于处理和净化JSON字符串,特别是针对跨站脚本攻击(XSS, Cross-Site Scripting)。 例如,在处理富文本内容、用户评论、从第三方服务获取的数据时,使用 JsonSa…...
K8S测试pod
背景 用于测试ping,curl等类型的pod Centos pod apiVersion: apps/v1 kind: Deployment metadata:name: centos-deploymentlabels:app: centos spec:replicas: 1selector:matchLabels:app: centostemplate:metadata:labels:app: centosspec:containers:- name: c…...
序章 熟悉战场篇—了解vue的基本操作
了解vue 的基本目录: dist 是打包后存放的目录(打包目录后续可以改)node_modules 是依赖包public 是静态index页面src 是存放文件的目录assets 是存放静态资源的目录components 是存放组件的目录views 是存放页面文件的目录(没有views 自己新建一个&…...
MongoDB聚合:$bucketAuto
按照指定的表达式对输入文档进行分类后放入指定数字的桶中,跟$bucket不太一样,$bucketAuto可以指定分组的数量(颗粒度),$bucketAuto会根据groupBy的值和颗粒度自动生成桶的边界。 语法 {$bucketAuto: {groupBy: <…...
认识监控系统zabbix
利用一个优秀的监控软件,我们可以: ●通过一个友好的界面进行浏览整个网站所有的服务器状态 ●可以在 Web 前端方便的查看监控数据 ●可以回溯寻找事故发生时系统的问题和报警情况 了解zabbix zabbix是什么? ●zabbix 是一个基于 Web 界面的提供分布…...
东北编程语言???
在GitHub闲逛,偶然发现了东北编程语言: 东北编程语言是由Zhanyong Wan创造的,它使用东北方言词汇作为基本关键字。这种编程语言的特点是简单易懂,适合小学文化程度的人学习,并且易于阅读、编写和记忆。它的语法与其他编…...
React16源码: React中的schedule调度整体流程
schedule调度的整体流程 React Fiber Scheduler 是 react16 最核心的一部分,这块在 react-reconciler 这个包中这个包的核心是 fiber reconciler,也即是 fiber 结构fiber 的结构帮助我们把react整个树的应用,更新的流程,能够拆成…...
springboot mybatis-plus swing实现报警监听
通过声音控制报警器,实现声光报警,使用beautyeye_lnf.jar美化界面如下 EnableTransactionManagement(proxyTargetClass true) SpringBootApplication EnableScheduling public class AlarmWarnApplication {public static void main(String[] args) …...
【计算机网络】网络层——详解IP协议
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得,欢迎大家在评论区交流讨论💌 目录 🐱一、I…...
【Java数据结构】03-二叉树,树和森林
4 二叉树、树和森林 重点章节,在选择,填空,综合中都有考察到。 4.1 掌握二叉树、树和森林的定义以及它们之间的异同点 1. 二叉树(Binary Tree) 定义: 二叉树是一种特殊的树结构,其中每个节点…...
C++ constexpr 在工程中的应用场景
C constexpr 在工程中的应用场景 在现代C开发中,constexpr关键字因其强大的编译时计算能力,逐渐成为提升性能与代码可维护性的利器。它允许开发者在编译期完成复杂的计算和初始化,从而减少运行时开销,同时增强代码的静态安全性。…...
别再只盯着username了!CTF表单注入题中,用Sqlmap探测password等隐藏参数的高效技巧
突破思维定式:CTF表单注入中隐藏参数的高阶利用策略 在CTF竞赛的Web安全赛道上,SQL注入始终是选手们的必修课。但当我们反复练习username参数注入时,出题人早已在暗处微笑——他们知道大多数选手会形成路径依赖。我曾在一个省级CTF比赛中遇到…...
Cadence 617实战:手把手教你搞定电流镜负载差分放大器的仿真与优化
Cadence 617实战:手把手教你搞定电流镜负载差分放大器的仿真与优化 在模拟集成电路设计中,电流镜负载差分放大器是一个经典而重要的电路结构。它不仅出现在各类运算放大器的输入级,也是理解模拟电路设计原理的绝佳案例。本文将带你从工具实操…...
UNIT-00:Berserk Interface辅助数据库课程设计:从ER图到SQL
UNIT-00:Berserk Interface辅助数据库课程设计:从ER图到SQL 你是不是正在为数据库课程设计发愁?面对一个模糊的业务需求,要从零开始画出清晰的ER图,再设计出规范化的数据库模式,最后还要写出一堆建表和查询…...
3大实战技巧:专业级Python通达信数据接口深度应用指南
3大实战技巧:专业级Python通达信数据接口深度应用指南 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx 在量化投资和金融数据分析领域,获取稳定、全面且经济的数据是开展工作…...
OpenClaw配置备份指南:Qwen3.5-9B环境快速迁移与恢复方法
OpenClaw配置备份指南:Qwen3.5-9B环境快速迁移与恢复方法 1. 为什么需要备份OpenClaw配置? 上周我的主力开发机突然硬盘故障,导致辛苦配置了两个月的OpenClaw环境全部丢失。最痛苦的不是重装软件,而是那些精心调试的模型参数、技…...
视频抠像技术全解析:基于MatAnyone的动态场景处理与多目标分离方案
视频抠像技术全解析:基于MatAnyone的动态场景处理与多目标分离方案 【免费下载链接】MatAnyone MatAnyone: Stable Video Matting with Consistent Memory Propagation 项目地址: https://gitcode.com/gh_mirrors/ma/MatAnyone 视频抠像技术在影视制作、直播…...
自动化数据标注:OpenClaw+Qwen3.5-9B加速AI模型训练
自动化数据标注:OpenClawQwen3.5-9B加速AI模型训练 1. 数据标注的痛点与自动化机遇 作为一名长期奋战在机器学习一线的开发者,我深知数据标注环节的折磨。去年参与一个图像分类项目时,团队花费了整整三周时间手工标注5万张图片,…...
电子工程师职业发展路径与技术能力提升指南
电子工程师职业发展全景指南1. 职业发展路径解析1.1 新手工程师常见困境与突破电子行业新入行者普遍面临"三无"困境:无事可做、无人指导、工作缺乏技术含量。这种矛盾心态源于三个关键因素:行业认知不足:缺乏对电子产业链的全局认识…...
Unity游戏多语言实时翻译解决方案:XUnity Auto Translator全解析
Unity游戏多语言实时翻译解决方案:XUnity Auto Translator全解析 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 在全球化游戏市场中,语言障碍成为制约玩家体验的关键因素。XUnity…...
