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) 定义: 二叉树是一种特殊的树结构,其中每个节点…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
