masterkong / blog

文章区
21 stars 2 forks source link

写给自己看的React-Redux源码实现 #9

Open masterkong opened 6 years ago

masterkong commented 6 years ago

写给自己看的React-Redux源码实现

前言

汇总整理下react-redux的实现原理,常看常新

react-redux

Provider

import {Provider} from 'react-redux';

实现原理是 context,注意这里使用的是旧版的Context实现。在React v16.3之后提供一种全新的Context API。

export default class Provider extends Component {
    //子组件需要实现这里,所以Provider也可以是子组件
    static contextTypes = {
        store: PropTypes.object,
        children: PropTypes.any,
    };

    static childContextTypes = {
        store: PropTypes.object,
    };

    getChildContext = () => {
        return { store: this.props.store, };
    };

    render () {
        return (<div>{this.props.children}</div>);
    }
}

connect

import {connect} from 'react-redux';

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

//简易版
const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
    class Connect extends Component {
        static contextTypes = {
            store: PropTypes.object,
        };

        constructor() {
            super();
            this.state = { allProps: {} }
        }

        componentWillMount() {
            const { store } = this.context;
            this._updateProps();
            store.subscribe(this._updateProps);
        }

        _updateProps = () => {
            const { store } = this.context;
            let stateProps = mapStateToProps(store.getState());
            let dispatchProps = mapDispatchToProps(store.dispatch);
            this.setState({
                allProps: {
                    ...stateProps,
                    ...dispatchProps,
                    ...this.props,
                }
            });
        };

        render () {
            return <WrappedComponent {...this.state.allProps} />
        }
    }

    return Connect;
};

export default connect;

redux

import {createStore,combinReducers,applyMiddleware,compose} from 'redux';

createStore

Store是一个由createStore创建,能提供getState,dispatch,subscribe方法,内部存储数据state的仓库。

其实还有一个比较少用的replaceReducer。

createStore(reducer, [preloadedState], enhancer)官方版实现

简易版实现

export const createStore = (reducer) => {
    let state = {};
    const listeners = [];
    const getState = () => state;
    const dispatch = (action) => {
        state = reducer(state, action);
        listeners.forEach((listener) => listener());
    };
    const subscribe = (listener) => listeners.push(listener);

    return {
        getState,
        dispatch,
        subscribe,
    };
};

compose

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

applyMiddleware及中间件

中间件的顺序是从左到右 applyMiddleware(...middlewares)官方版实现

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

Redux的中间件本质上就是增强dispatch,中间件要满足两个特性:一是扩展功能,二是可以被链式调用。

//中间件标准写法
function doNothingMiddleware({dispatch, getState}){
    return function(next){
        return function(action){
            return next(action);
        }
    }
}

//redux-thunk的实现
ffunction createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

参考文献

Redux介绍 Redux 中文文档 盘点 React 16.0 ~ 16.5 主要更新及其应用