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 = () => 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

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 StoreState
      return isState ? v.value : v
    },
    set: () => store,
  })
  storePool[id] = { store }
  const useStore = () => proxy
  return useStore
}

class StoreState {
  constructor(value) {
    this.value = value
    this.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.value
      const symbol = storeState.symbol
      Object.defineProperty(storeState, 'value', {
        get: () => {
          return value
        },
        set: (newValue) => {
          value = newValue
          updateView(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] = listeners
  return () => (listenersPool[symbol] = listenersPool[symbol].filter((l) => l !== f))
}
const updateView = (symbol) => {
  if (typeof symbol !== 'symbol') return
  const 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].store
      const proxy = new Proxy(store, {
        get: (target, prop) => {
          const v = target[prop]
          const isState = v instanceof StoreState
          if (isState) {
            const symbol = v.symbol
            if (!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

相关推荐

  1. react js定义实现状态管理

    2024-01-18 05:52:02       27 阅读
  2. React状态管理详解

    2024-01-18 05:52:02       36 阅读
  3. React使用Valtio的hook实现响应式状态管理

    2024-01-18 05:52:02       34 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-18 05:52:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-18 05:52:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-18 05:52:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-18 05:52:02       20 阅读

热门阅读

  1. codeforces A -Cut Ribbon

    2024-01-18 05:52:02       31 阅读
  2. 数据结构与算法-二叉树序列化和反序列化

    2024-01-18 05:52:02       28 阅读
  3. 无root权限安装git-lfs(linux版)

    2024-01-18 05:52:02       33 阅读
  4. sqlserver2012 跨服务器查询

    2024-01-18 05:52:02       29 阅读
  5. 【SpringBoot系列】AOP详解

    2024-01-18 05:52:02       26 阅读
  6. 解析XML字符串并存入数据库

    2024-01-18 05:52:02       27 阅读
  7. CSS 盒模型概述

    2024-01-18 05:52:02       31 阅读
  8. Tomcat Notes: URL Mapping

    2024-01-18 05:52:02       29 阅读
  9. 【Flutter】关注的那些点

    2024-01-18 05:52:02       36 阅读
  10. 前端基础:回顾es6相关知识

    2024-01-18 05:52:02       35 阅读