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 = () => state
const 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() * 1000
return state
default:
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.store
return <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.store
const 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 = value
this.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 StoreState
return 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.value
Object.defineProperty(storeState, 'value', {
get: () => value,
set: (newValue) => {
value = newValue
updateView()
},
})
}
})
}
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文章来源:https://www.toymoban.com/news/detail-800162.html
react-pinia
实现模块化文章来源地址https://www.toymoban.com/news/detail-800162.html
react-pinia.js
import { useEffect, useState } from 'react'
const storeMap = new Map()
export function createStore(id, f) {
if (typeof f !== 'function') throw new Error('Expected f to be a function.')
const store = f()
store._storeId = id
const proxy = new Proxy(store, {
get: (target, p) => (target[p] instanceof StoreState ? target[p].value : target[p]),
set: () => store,
})
storeMap.set(id, store)
return () => proxy
}
class StoreState {
constructor({ value }) {
this.value = value
this.symbol = Symbol()
}
}
export const useStoreState = (value) => {
const state = new StoreState({ value })
return new Proxy(state, {
get: (target, p) => target[p],
set: (target, p, value) => {
target[p] = value
target instanceof StoreState && updateView(target.symbol)
return state
},
})
}
const listenerMap = new Map()
const updateView = (symbol) => {
const listeners = listenerMap.get(symbol) || []
listeners.forEach((listener) => {
listener()
})
}
const subscribe = (symbol, listener) => {
if (!listenerMap.has(symbol)) listenerMap.set(symbol, new Set())
listenerMap.get(symbol).add(listener)
return () => {
listenerMap.get(symbol).delete(listener)
}
}
const createCollectDepend = () => {
const symbolArr = []
const f = (storeProxy) => {
const id = storeProxy._storeId
const store = storeMap.get(id)
return new Proxy(store, {
get: (target, p) => {
if (target[p] instanceof StoreState) {
symbolArr.push(target[p].symbol)
return target[p].value
}
return target[p]
},
set: () => store,
})
}
return [symbolArr, f]
}
export const collectDepend = (f) => {
if (typeof f !== 'function') throw new Error('Expected f to be a function.')
const [symbolArr, collectFunc] = createCollectDepend()
return (Cpn) => {
return (props) => {
const mapStore = f(collectFunc) || {}
const mapStoreIsObj = Object.prototype.toString.call(mapStore) === '[object Object]'
const nextState = mapStoreIsObj ? mapStore : {}
const [, forceUpdate] = useState(Symbol())
useEffect(() => {
const unSubscribeArr = symbolArr.map((id) => subscribe(id, () => forceUpdate(Symbol())))
return () => unSubscribeArr.forEach((unSubscribe) => unSubscribe())
}, [])
return <Cpn {...props} {...nextState} />
}
}
}
使用
import React, { useState, memo } from 'react'
import { createStore, useStoreState, collectDepend } from './react-pinia'
export default function Demo() {
console.log('render Demo');
const [n, setN] = useState(0)
const test = () => {
setN(Math.random())
}
return (
<div>
<h2>{n}</h2>
<button onClick={test} >test</button>
<Test />
</div>
)
}
const store = createStore('user', () => {
const name = useStoreState('张三')
const change = () => {
name.value = '李四' + Math.random()
}
return { name, change }
})()
function Test2(props) {
console.log('render Test2');
return (
<div>
<h2>Test {props.name}</h2>
<button onClick={props.change}>change</button>
<br />
</div>
)
}
const Test = memo(collectDepend((collectFunc) => {
const userStore = collectFunc(store)
return { name: userStore.name, change: userStore.change }
})(Test2))
到了这里,关于react js自定义实现状态管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!