react js自定义实现状态管理

redux基础实现

myRedux

javascript 复制代码
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,
  }
}

使用

javascript 复制代码
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

javascript 复制代码
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} />
    }
  }
}

使用

javascript 复制代码
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

javascript 复制代码
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} />
  }
}

使用

javascript 复制代码
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

相关推荐
bin91531 小时前
DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)
前端·javascript·vue.js·ecmascript·deepseek
勤奋的凯尔森同学1 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
丁卯4042 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
晴空万里藏片云2 小时前
elment Table多级表头固定列后,合计行错位显示问题解决
前端·javascript·vue.js
曦月合一2 小时前
html中iframe标签 隐藏滚动条
前端·html·iframe
奶球不是球2 小时前
el-button按钮的loading状态设置
前端·javascript
kidding7233 小时前
前端VUE3的面试题
前端·typescript·compositionapi·fragment·teleport·suspense
人间打气筒(Ada)3 小时前
MySQL主从架构
服务器·数据库·mysql
无责任此方_修行中4 小时前
每周见闻分享:杂谈AI取代程序员
javascript·资讯