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

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…...

行为型设计模式——中介者模式

中介者模式 中介者模式主要是将关联关系由一个中介者类统一管理维护&#xff0c;一般来说&#xff0c;同事类之间的关系是比较复杂的&#xff0c;多个同事类之间互相关联时&#xff0c;他们之间的关系会呈现为复杂的网状结构&#xff0c;这是一种过度耦合的架构&#xff0c;即…...

通信行业无线基本概念

fast roaming&#xff08;快速漫游&#xff09;&#xff1a;使用户在不同的基站&#xff08;access point&#xff09;间可以平滑的切换&#xff0c;在802.11r协议标准中定义。band steering&#xff08;波段转向&#xff09;&#xff1a;在双频段&#xff08;2.4G和5G&#xf…...

grep 在运维中的常用可选项

一、对比两个文件 vim -d <filename1> <filename2> 演示&#xff1a; 需求&#xff1a;&#xff5e;目录下有两个文件一个test.txt 以及 text2.txt,需求对比两个文件的内容。 执行后会显示如图&#xff0c;不同会高亮。 二、两次过滤 场景&#xff1a;当需要多…...

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测试报告的区别&#xff0c;检测认证行业6年老司机 UL认证是自愿性的认证&#xff0c;需要检测产品和审核工厂&#xff0c;每个季度审核一次&#xff0c;费用高、时间久&#xff0c;而且审厂非常的严格。 UL测试报告是根据产品选用相应…...

鸿蒙HarmonyOS-SDK管理使用指南

鸿蒙HarmonyOS-SDK管理使用指南 文章目录 鸿蒙HarmonyOS-SDK管理使用指南sdkmgr使用指导查看SDK组件安装组件卸载组件查看sdkmgr版本查看sdkmgr帮助options选项说明ohsdkmgr使用指导查看SDK组件安装组件卸载组件查看ohsdkmgr版本查看ohsdkmgr帮助option...

QT上位机开发(进度条操作)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 进度条是一个比较常见的控件。如果某个操作需要很长的时间才能完成&#xff0c;那么这个时候最好有一个进度条提示&#xff0c;这样比较容易平复一…...

637_二叉树的层平均值

描述 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受 思路 无需思路&#xff0c;乱杀 解答 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, righ…...

Ubuntu20.4 Mono C# gtk 编程习练笔记(三)

Mono对gtk做了很努力的封装&#xff0c;即便如此仍然与System.Windows.Form中的控件操作方法有许多差异&#xff0c;这是gtk本身特性或称为特色决定的。下面是gtk常用控件在Mono C#中的一些用法。 Button控件 在工具箱中该控件的clicked信号双击后自动生成回调函数prototype&…...

What is `JsonSanitizer.sanitize` does?

JsonSanitizer.sanitize 是一个Java库中的方法&#xff0c;用于处理和净化JSON字符串&#xff0c;特别是针对跨站脚本攻击&#xff08;XSS, Cross-Site Scripting&#xff09;。 例如&#xff0c;在处理富文本内容、用户评论、从第三方服务获取的数据时&#xff0c;使用 JsonSa…...

K8S测试pod

背景 用于测试ping&#xff0c;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 的基本目录&#xff1a; dist 是打包后存放的目录(打包目录后续可以改)node_modules 是依赖包public 是静态index页面src 是存放文件的目录assets 是存放静态资源的目录components 是存放组件的目录views 是存放页面文件的目录&#xff08;没有views 自己新建一个&…...

MongoDB聚合:$bucketAuto

按照指定的表达式对输入文档进行分类后放入指定数字的桶中&#xff0c;跟$bucket不太一样&#xff0c;$bucketAuto可以指定分组的数量&#xff08;颗粒度&#xff09;&#xff0c;$bucketAuto会根据groupBy的值和颗粒度自动生成桶的边界。 语法 {$bucketAuto: {groupBy: <…...

认识监控系统zabbix

利用一个优秀的监控软件&#xff0c;我们可以: ●通过一个友好的界面进行浏览整个网站所有的服务器状态 ●可以在 Web 前端方便的查看监控数据 ●可以回溯寻找事故发生时系统的问题和报警情况 了解zabbix zabbix是什么&#xff1f; ●zabbix 是一个基于 Web 界面的提供分布…...

东北编程语言???

在GitHub闲逛&#xff0c;偶然发现了东北编程语言&#xff1a; 东北编程语言是由Zhanyong Wan创造的&#xff0c;它使用东北方言词汇作为基本关键字。这种编程语言的特点是简单易懂&#xff0c;适合小学文化程度的人学习&#xff0c;并且易于阅读、编写和记忆。它的语法与其他编…...

React16源码: React中的schedule调度整体流程

schedule调度的整体流程 React Fiber Scheduler 是 react16 最核心的一部分&#xff0c;这块在 react-reconciler 这个包中这个包的核心是 fiber reconciler&#xff0c;也即是 fiber 结构fiber 的结构帮助我们把react整个树的应用&#xff0c;更新的流程&#xff0c;能够拆成…...

springboot mybatis-plus swing实现报警监听

通过声音控制报警器&#xff0c;实现声光报警&#xff0c;使用beautyeye_lnf.jar美化界面如下 EnableTransactionManagement(proxyTargetClass true) SpringBootApplication EnableScheduling public class AlarmWarnApplication {public static void main(String[] args) …...

【计算机网络】网络层——详解IP协议

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 &#x1f431;一、I…...

【Java数据结构】03-二叉树,树和森林

4 二叉树、树和森林 重点章节&#xff0c;在选择&#xff0c;填空&#xff0c;综合中都有考察到。 4.1 掌握二叉树、树和森林的定义以及它们之间的异同点 1. 二叉树&#xff08;Binary Tree&#xff09; 定义&#xff1a; 二叉树是一种特殊的树结构&#xff0c;其中每个节点…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...