美文网首页
Redux 家族(一):实战和源码实现

Redux 家族(一):实战和源码实现

作者: 梦想成真213 | 来源:发表于2021-01-30 18:36 被阅读0次

    每一种新语言,新框架或者工具的出现都是为了解决项目中存在的问题。

    动机

    随着 Javascript 单页应用的开发越来越复杂,管理不断变化的 state 变得非常困难。这些状态有很多:UI 状态,服务器数据,缓存数据等。而组件,页面之间状态共享也是一个问题。

    在 React 中,数据在组件中是单向流动的,数据从父组件传递给子组件是通过 props,由于这个特征,两个非父子关系的组件(或叫兄弟组件)之间的通信就比较麻烦。

    Redux 的出现正式是为了解决 state 数据的管理问题。

    设计思想

    Redux 将整个应用的状态都存储在一个地方,就是 Store(容器),里面保存着应用的 state(状态树),组件可以通过 dispach(派发)action(行为)给 store(状态容器),而不是直接通知其他组件。其他组件可以通过订阅 store 中的 state(状态)来更新视图。


    三大原则

    写 Redux 需要遵循三个原则,有制约,有规则,状态才会可预测。因此 Redux 需要遵循以下三个原则:

    单一数据源

    整个应用的 state 被储存在一棵 Object tree 中,并且这个 Object tree 只存在于唯一一个 store 中;

    State 是只读

    State 是只读的,惟一改变 state 的方法就是dispatch(触发) action(动作);

    使用纯函数来执行修改

    通过 disptch action 来改变 state,action 是一个用于描述已发生事件的普通对象,使用纯函数(没有副作用的函数:函数的输出完全取决函数的参数;副作用:获取网络请求数据,修改 dom 标题等都属于副作用)来执行修改,为了描述 action 如何改变 state tree ,需要编写 reducer (处理器)。

    单一数据源的设计让组件之间的通信更加方便,同时也便于状态的统一管理。


    基本使用

    通过一个 React 计数器组件来使用一下,新建一个项目,删除多余的文件,启动:

    npx create-react-app redux-family
    cd redux-family
    npm install redux -S
    npm start
    

    编写一个 Counter 组件,应用 redux :
    通过使用createStore来创建一个srore仓库,参数就是reducer初始 state,通过 store.getState()方法获取状态,store.dispatch来派发动作,store. subscribe订阅更新,store.unsubscribe取消订阅,这些基本就是 redux 的全部了:

    // src/components/Counter1.js
    import React, { Component } from 'react';
    import { createStore} from 'redux';
    
    const INCREMENT = 'ADD';
    const DECREMENT = 'MINUS';
    
    const reducer = (state = initState, action) => {
      switch (action.type) {
        case INCREMENT:
          return { number: state.number + 1 };
        case DECREMENT:
          return { number: state.number - 1 };
        default:
          return state;
        }
    }
    
    let initState = { number: 0 };
    
    const store = createStore(reducer, initState);
    
    export default class Counter extends Component {
        unsubscribe;
        constructor(props) {
          super(props);
          this.state = { number: 0 };
        }
        componentDidMount() {
          this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().number }));
        }
        componentWillUnmount() {
          this.unsubscribe();
        }
        render() {
          return (
            <div>
              <p>{this.state.number}</p>
              <button onClick={() => store.dispatch({ type: 'ADD' })}>+</button>
              <button onClick={() => store.dispatch({ type: 'MINUS' })}>-</button>
              <button onClick={
                () => {
                    setTimeout(() => {
                        store.dispatch({ type: 'ADD' });
                    }, 1000);
                }
              }>1秒后加1</button>
            </div>
          )
        }
    }
    
    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import Counter1 from './components/Counter1';
    
    ReactDOM.render(
      <Counter1 />,
      document.getElementById('root')
    );
    

    结合 React 框架 react-redux

    react-redux 用来整合 react 状态和数据订阅,使其使用起来更简便。提供 Provider使所有组件都可以获取状态,connect联接状态和组件,获取state状态,dispatch派发动作:

    npm install react-redux -S
    npm start
    
    // src/components/Counter2.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class Counter extends Component {
        render() {
          return (
            <div>
              <p>{this.props.number}</p>
              <button onClick={() => this.props.add()}>+</button>
              <button onClick={() => this.props.minus()}>-</button>
              <button onClick={
                () => {
                    setTimeout(() => {
                      this.props.add({ type: 'ADD' });
                    }, 1000);
                }
              }>1秒后加1</button>
            </div>
          )
        }
    }
    const mapStateToProps = (state) => {
      return {
        number: state.number
      }
    }
    const mapDispatchToProps = (dispatch) => {
      return {
        add: () => dispatch({type: 'ADD'}),
        minus: () => dispatch({type: 'MINUS'})
      };
    }
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(Counter)
    
    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { createStore} from 'redux';
    import { Provider } from 'react-redux'
    import Counter2 from './components/Counter2'
    
    const INCREMENT = 'ADD';
    const DECREMENT = 'MINUS';
    
    const reducer = (state = initState, action) => {
      switch (action.type) {
        case INCREMENT:
          return { number: state.number + 1 };
        case DECREMENT:
          return { number: state.number - 1 };
        default:
          return state;
        }
    }
    
    let initState = { number: 0 };
    
    const store = createStore(reducer, initState);
    
    ReactDOM.render(
      <Provider store={store}>
        <Counter2 />
      </Provider>,
      document.getElementById('root')
    );
    

    不仅仅使用在 React 上

    Redux 是一个状态数据流管理方案,不是为了 react 单独实现的,可以使用在任何框架以及原生 Javascript 中,这更体现了 Redux 的广阔的视角和先进的思想。

    下面在原生 Javascript 应用中使用如下:
    需要修改下 index.html,增加显示数据的 dom,以及操作按钮的 dom:

    // index.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>React App</title>
      </head>
      <body>
        <div id="root"></div>
        <div id="counter">
          <p id="number">0</p>
          <button id="add">+</button>
          <button id="minus">-</button>
        </div>
      </body>
    </html>
    

    index.js文件中通过订阅渲染函数来做数据的更新:

    // src/index.js
    import { createStore} from 'redux';
    
    let counterValue = document.getElementById('number');
    let incrementBtn = document.getElementById('add');
    let decrementBtn = document.getElementById('minus');
    
    const INCREMENT = 'INCREMENT';
    const DECREMENT = 'DECREMENT';
    let initState = { number: 0 };
    
    const reducer = (state = initState, action) => {
      switch (action.type) {
        case INCREMENT:
          return { number: state.number + 1 };
        case DECREMENT:
          return { number: state.number - 1 };
        default:
          return state;
      }
    }
    
    let store = createStore(reducer);
    
    function render() {
      counterValue.innerHTML = store.getState().number;
    }
    
    store.subscribe(render);
    
    render();
    
    incrementBtn.addEventListener('click', function () {
      store.dispatch({ type: INCREMENT });
    });
    decrementBtn.addEventListener('click', function () {
      store.dispatch({ type: DECREMENT });
    });
    

    以上就是Redux的基本应用,学会使用一个框架很快,但是不能仅限于使用,还要知道其内部的实现原理,下面就一起来写一个具有简单功能的模仿的 redux

    脱离框架的简单实现(源码)

    createStore 创建仓库

    从使用中可以看出,redux需要返回一个createStore方法,这个方法返回一个对象,对象上有getState(),dispatch,subscribe方法,getState()返回最新的statedispatch需要派发actionsubscribe需要订阅更新函数并返回一个取消订阅的函数redux内部需要先派发一次INIT动作填充初始状态。如此分析下来,这样一个redux壳就有了,先来写壳,再往里面填充功能:
    redux/index.js

    import createStore from './createStore.js'
    
    export {
      createStore
    }
    

    redux/utils/ActionType.js

    /**
     * These are private action types reserved(保留的) by Redux.
     * For any unknown actions, you must return the current state.
     * (所有未知的action都必须返回当前的state)
     * If the current state is undefined, you must return the initial state.
     * (如果当前state是undefined,必须返回和一个初始的state)
     * Do not reference these action types directly in your code.
     * (不要在代码中直接引入这些action type)
     */
    
    const randomString = () => {
      return Math.random().toString(36).substring(7).split('').join('.')
    }
    
    const ActionTypes = {
      INIT: `@@redux/INIT${randomString()}`
    }
    
    export default ActionTypes
    

    redux/createStore.js

    import ActionType from './utils/ActionType'
    
    function createStore(reducer, preloadedState) {
      let currentState = preloadedState || {}
      let currentReducer = reducer
      let currentListeners = []
    
      const getState = () => {
        return currentState
      }
      const dispatch = (action) => {
        
      }
    
      const subscribe = (listener) => {
        return function unsubscribe() {
        }
      }
    
      // 内部需要先派发一次动作
      // When a store is created, an "INIT" action is dispatched so that every
      // reducer returns their initial state. This effectively populates(填充)
      // the initial state tree.
    
      dispatch({type: ActionType.INIT})
    
      return {
        getState,
        dispatch,
        subscribe,
      }
    }
    
    export default createStore
    

    这样的壳子就写好了,现在填充功能,订阅函数需要收集所有的订阅事件存储到数组中,并返回一个取消订阅的函数;
    dispatch派发actionreducer获取最新的状态,最后通知所有的订阅组件(这里是更新函数);

    const subscribe = (listener) => {
        currentListeners.push(listener)
        // 返回取消订阅的函数
        return function unsubscribe() {
          const index = currentListeners.indexOf(listener)
          // 取消订阅就是将这个订阅事件从数组中删除
          currentListeners.splice(index, 1)
        }
      }
    const dispatch = (action) => {
        // reducer:传入旧的状态和action,返回新的state
        currentState = currentReducer(currentState, action)
    
        const listeners = currentListeners
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          // 执行每一个订阅函数
          listener()
        }
        return action
      }
    

    这样基本的redux功能就实现完了,将原生 Javascript 应用改成自己实现的redux,功能依然正常。

    bindActionCreator

    使用action creator直接派发动作,不直接调用 dispatch,这是一种更便捷的方法。bindActionCreatoraction creator 转换为带有相同key,但是每个函数都包含dispatch调用。

    将之前直接在 react 中使用 redux 的例子重新改写一下,使用 bindActionCreator 重写一下实现:

    import React, { Component } from 'react';
    import { createStore, bindActionCreators} from 'redux';
    
    const INCREMENT = 'ADD';
    const DECREMENT = 'MINUS';
    
    const reducer = (state = initState, action) => {
      switch (action.type) {
        case INCREMENT:
          return { number: state.number + 1 };
        case DECREMENT:
          return { number: state.number - 1 };
        default:
          return state;
        }
    }
    
    + function Add() {
    +  return { type: INCREMENT }
    +}
    +function Minus() {
    +  return { type: DECREMENT }
    +}
    
    let initState = { number: 0 };
    const store = createStore(reducer, initState);
    + const actions = { Add, Minus }
    + const boundActions = bindActionCreators(actions, store.dispatch)
    
    
    export default class Counter extends Component {
        unsubscribe;
        constructor(props) {
          super(props);
          this.state = { number: 0 };
        }
        componentDidMount() {
          this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().number }));
        }
        componentWillUnmount() {
          this.unsubscribe();
        }
        render() {
          return (
            <div>
              <p>{this.state.number}</p>
    +           <button onClick={boundActions.Add}>+</button>
    +           <button onClick={boundActions.Minus}>-</button>
              <button onClick={
                () => {
                    setTimeout(() => {
    +                   boundActions.Add();
                    }, 1000);
                }
              }>1秒后加1</button>
            </div>
          )
        }
    }
    

    可以看到所有 action 都由 action creator 派发,不再显式的调用store.dispatch,bindActionCreator 内部就是返回了 dispatch 函数的调用,下面我们来写一下这个 bindActionCreator 函数实现:

    思路:就是给每一个action creator 都返回 dispatch 方法调用,如果传入的是函数,也就是单个 action creator,直接调用返回 type,将参数传给调用 dispatch 的函数,如果是一个对象,就给这个对象上的每一个 action creator 都加上 dispatch 方法调用,所以代码的核心就是bindActionCreator

    function bindActionCreator(actionCreator, dispatch) {
      // 这里派发 action,执行 actionCreator 函数,返回一个 type 作为 dispatch 的参数调用
      return function(...args) {
        dispatch(actionCreator.apply(this, args))
      }
    }
    

    完整的实现代码如下:

    /**
     * function Add() {
          return { type: INCREMENT }
       }
       function Minus() {
          return { type: DECREMENT }
       }
     * const actions = { Add, Minus }
     * const boundActions = bindActionCreators(actions, store.dispatch)
     */
    
    function bindActionCreator(actionCreator, dispatch) {
      // 这里派发 action,执行 actionCreator 函数,返回一个 type 作为 dispatch 的参数调用的函数
      return function(...args) {
        dispatch(actionCreator.apply(this, args))
      }
    }
    
    function bindActionCreators(actionCreators, dispatch) {
      // 如果是一个函数,表示就是一个 actionCreator,直接返回包装了调用dispatch的函数
      if (actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    
      // 如果是一个对象,给每个 actionCreator 都包装返回调用dispatch的函数
      if (typeof actionCreators === 'object' && actionCreators !== null) {
        const bindActionCreators = {}
        for(const key in actionCreators) {
          const actionCreator = actionCreators[key]
          bindActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
        return bindActionCreators
      }
    }
    
    export default bindActionCreators
    

    combineReducers

    通常组件不会只有一个 reducer,每个组件都可以有自己的 reducer 和 state,便于模块化管理。但是 redux 规定,一个应用只能有一个 store,一个 reducer,一个 state,根据程序模块化,分而治之的管理模式,这个时候就需要合并多个 reducer,多个状态为一个 reducer,一个state,combineReducers 就是做合并 reducer操作的,下面先来看看用法,然后再实现一个。
    这里只贴重要的代码,其他细致的可以到 github 仓库看实战。
    src/components/Counter5.js

    import React, { Component } from 'react';
    import { bindActionCreators} from '../redux';
    import * as actionType from '../actionTypes'
    // import { bindActionCreators } from '../redux'
    import store from '../store'
    
    
    const actions = {
      Add5: actionType.Add5,
      Minus5: actionType.Minus5
    }
    const boundActions = bindActionCreators(actions, store.dispatch)
    
    export default class Counter5 extends Component {
      constructor(props) {
        super(props)
        this.state = { number : 5}
      }
      componentDidMount() {
        this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().Counter5.number }));
      }
      componentWillUnmount() {
        this.unsubscribe();
      }
      render() {
        return (
          <div>
            <p>{this.state.number}</p>
            <button onClick={boundActions.Add5}>+</button>
            <button onClick={boundActions.Minus5}>-</button>
            <button onClick={
              () => {
                  setTimeout(() => {
                    boundActions.Add5();
                  }, 1000);
              }
            }>1秒后加5</button>
          </div>
        )
      }
    }
    

    新建 reducers 和 action types
    src/reducers/Counter5.jssrc/actionTypes

    // src/reducers/Counter5.js
    import * as actionType from '../actionTypes'
    
    export let Counter5State = { number: 5 };
    
    const reducer = (state = Counter5State, action) => {
      switch (action.type) {
        case actionType.INCREMENT5:
          return { number: state.number + 5 };
        case actionType.DECREMENT5:
          return { number: state.number - 5 };
        default:
          return state;
        }
    }
    export default reducer
    
    // src/actionTypes.js
    export const INCREMENT5 = 'ADD5';
    export const DECREMENT5 = 'MINUS5';
    
    export const INCREMENT6 = 'ADD6';
    export const DECREMENT6 = 'MINUS6';
    
    export function Add5() {
      return { type: INCREMENT5 }
    }
    export function Minus5() {
      return { type: DECREMENT5 }
    }
    
    export function Add6() {
      return { type: INCREMENT6 }
    }
    export function Minus6() {
      return { type: DECREMENT6 }
    }
    
    

    src/store.js

    import { createStore } from 'redux'
    import reducers from './reducers'
    const store = createStore(reducers);
    export default store
    

    src/reducers/index.js

    import { combineReducers } from 'redux'
    import Counter5 from '../reducers/Counter5'
    import Counter6 from '../reducers/Counter6'
    
    const reducers = {
      Counter5,
      Counter6
    }
    const combinedReducers = combineReducers(reducers)
    export default combinedReducers
    

    其他的不贴了,效果就是组件的状态互不干扰:


    combineReducers就是将多个组件的 reducer 合并为一个 reducer,内部实现是过滤 reducers 对象上的函数属性,然后返回一个combination函数,这个combination函数内部会遍历过滤之后的对象,解析出来每一个 reducer,拿到对应的状态,最后再根据 reducer 的 key,返回一个新的状态对象。根据 reducer 解析出来的 state 是最重要的一段,const reducer = finalReducers[key]const nextStateForKey = reducer(previousStateForKey, action)。这里面有一个优化操作:定义一个 hasChanged的标志,新状态和老状态不一致就返回新状态,否则返回老的状态。
    具体代码实现如下,跟源码的实现是一致的,代码有备注解释:
    src/redux/combineReducers

    export default function combineReducers (reducers) {
      // 1. 过滤 reducers 对象上的函数属性
      const reducerKeys = Object.keys(reducers) // ["counter1", "counter2"]
      const finalReducers = {}
      for(let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i] // "counter1"
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key] // finalReducers: {counter1: counter1, counter2: counter2}
        }
      }
    
      // 2. 返回一个函数 遍历finalReducers,生成state
      const finalReducerKeys = Object.keys(finalReducers) // ['counter1', 'counter2']
      return function combination (state = {}, action) {
        let hasChanged = false // hasChanged是作性能优化,没有改变就返回原来的state
        const nextState = {}
        for(let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i] // "counter1"
          const reducer = finalReducers[key] // reducer counter1 函数
          const previousStateForKey = state[key] // 取出reducer对应的state
    
          // 这步最重要:给reducer传入老的state和action,返回新的state
          const nextStateForKey = reducer(previousStateForKey, action)
          // 将新的state的key 拼到总的state上,组件通过 state.counter1.number获取
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey // 前后状态不一样就是改变了
        }
        hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // reducers的key不一样也是变了
        return hasChanged ? nextState : state
      }
    

    以上就是 combineReducers 的实现。

    react-redux 实现

    从上面可以 Counter5,Counter6 组件可以看出来,这两个组件其实有很多的相似代码,比如订阅更新,获取状态等,这些都可以抽离出来,减少冗余代码,将 store 绑定到 context 上,至上而下的传递,这就是 react-redux 做的事情。

    先来看一下 react-redux 的应用,实现一个 Counter7 组件,对应的 actions 和 reducer 和上面一样,就不赘述了。Counter7 使用 react-redux 的 connect 返回一个高阶组件

    src/components/Counter7.js

    import React, { Component } from 'react'
    import * as actionType from '../actionTypes'
    import { connect } from 'react-redux'
    
    
    const actions = {
      add: actionType.Add7,
      minus: actionType.Minus7
    }
    
    const mapStateToProps = (state) => {
      console.log(state)
      return {
        number: state.Counter7.number
      }
    }
    
    const mapDispatchToProps = actions
    
    class Counter7 extends Component {
      render() {
        const { number, add, minus } = this.props
        return (
          <div>
            <p>{number}</p>
            <button onClick={add}>+</button>
            <button onClick={minus}>-</button>
            <button onClick={
              () => {
                  setTimeout(() => {
                    add()
                  }, 1000)
              }
            }>1秒后加5</button>
          </div>
        )
      }
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps 
    )(Counter7)
    

    src/index.js 使用 Provider 共享 store:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Counter7 from './components/Counter7'
    import store from './store'
    import { Provider } from 'react-redux'
    
    ReactDOM.render(
      <Provider store={store}>
        <Counter7 />
      </Provider>
    ,
      document.getElementById('root')
    )
    

    可以看到 Counter7 使用 connect 连接,mapStateToProps 将 store 上的state 拼到 props 上mapDispatchToProps 将派发函数拼接到 props
    下面来看看 Provider 和 connect 实现,根据 react-redux 官方的实现,我们也采用函数和 hooks 的方式实现。

    Provider 是利用 react 的 context 创建上下文容器,value 可以设置成共享的数据,provider 提供数据,consumer 消费这些数据。
    const { Provider, Consumer } = React.createContext(defaultValue);
    新建一个 react-redux 文件目录,里面来写代码实现:
    src/react-redux/index.js

    import Provider from './Provider'
    import connect from './connect'
    
    export {
      Provider,
      connect
    }
    

    src/react-redux/ReactReduxContext.js

    import React from 'react'
    export const ReactReduxContext = React.createContext(null)
    export default ReactReduxContext
    

    src/react-redux/Provider.js

    import React from 'react'
    import ReactReduxContext from './ReactReduxContext'
    
    export default function Provider (props) {
      return (
        <ReactReduxContext.Provider value={{ store: props.store }}>
          {props.children}
        </ReactReduxContext.Provider>
      )
    }
    

    最复杂的是 connnect 的实现:
    connnect 是一个函数,返回一个被包装过后的组件,里面做的事情就是将 state,dispatch 派发函数作为 props 拼到被包装的组件上,然后在渲染的时候订阅更新函数,更新状态。
    mapStateToProps 映射 state 到 props 上const stateProps = useMemo(() => mapStateToProps(preState), [preState])

    const mapStateToProps = (state) => {
      return {
        number: state.Counter7.number
      }
    }
    

    mapDispatchToProps 就是映射 dispatch 函数到 props,这里分三种情况,如果参数是对象就要做 bindActionCreators,分别包装返回具有 dispatch能力的函数对象,像这样:{add: ƒ, minus: ƒ};如果参数是函数,就直接拼mapDispatchToProps(props, dispatch),否则就直接返回一个包含 dispatch 的对象;

    const dispatchProps = useMemo(() => {
      let dispatchProps
      if (typeof mapDispatchToProps === 'object') {
        dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
       } else if (typeof mapDispatchToProps === 'function') {
        dispatchProps = mapDispatchToProps(props, dispatch)
        } else {
        dispatchProps = { dispatch }
        }
        return dispatchProps
    }, [dispatch, props])
    

    然后订阅更新函数,更新状态:

    const [, forceUpdate] = useReducer(x => x + 1, 0)
    useLayoutEffect(() => subscribe(forceUpdate), [subscribe])
    

    useLayoutEffect 会在 dom 更新完之后,浏览器绘制之前执行。
    最后返回包装之后的组件:
    return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />
    完整的 connect 的实现代码如下:

    import React, { useContext, useMemo, useReducer, useLayoutEffect } from 'react'
    import ReactReduxContext from './ReactReduxContext'
    import { bindActionCreators } from '../redux'
    
    export default function connect (mapStateToProps, mapDispatchToProps) {
      return function (WrappedComponent) {
        return function (props) {
          const { store } = useContext(ReactReduxContext)
          const { getState, dispatch, subscribe} = store
          const preState = getState()
          // 映射状态到 props
          const stateProps = useMemo(() => mapStateToProps(preState), [preState])
    
          // 映射 dispatch 到 props
          const dispatchProps = useMemo(() => {
            let dispatchProps
            if (typeof mapDispatchToProps === 'object') {
              dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
            } else if (typeof mapDispatchToProps === 'function') {
              dispatchProps = mapDispatchToProps(props, dispatch)
            } else {
              dispatchProps = { dispatch }
            }
            return dispatchProps
          }, [dispatch, props])
    
          // 订阅更新函数,更新状态
          const [, forceUpdate] = useReducer(x => x + 1, 0)
          useLayoutEffect(() => subscribe(forceUpdate), [subscribe])
    
          // 返回被包装的组件
          return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />
        }
      }
    }
    

    到目前为止,redux 和 react-redux 的基本核心都实现了,参照的是 github 上的源码。还剩下一个 中间件的功能,这个准备在下一章单独去写,分析一下时常用的几个 redux 中间件用法和实现,以及连接中间件的实现原理,到 react 生态的整合,比如 redux-saga,dva,umi等。目前项目用到的生态就是这些,所以想单独开一篇专门写这个(希望不要开天窗),加深理解,温故知新。

    参考:
    https://github.com/reduxjs/redux
    https://github.com/reduxjs/react-redux

    相关文章

      网友评论

          本文标题:Redux 家族(一):实战和源码实现

          本文链接:https://www.haomeiwen.com/subject/xvqrnktx.html