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> …...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
