redux

https://github.com/reduxjs

实现 createStore

  • https://github.com/reduxjs/redux/blob/master/src/createStore.ts
    src/redux/index.js
    1. export {default as createStore} from './createStore'
    2. export {default as bindActionCreators} from './bindActionCreators'
    3. export {default as combineReducers} from './combineReducers'
    4. export {default as compose} from './compose'
    5. export {default as applyMiddleware} from './applyMiddleware'
    src/redux/createStore.js
    ```jsx import ActionTypes from ‘./utils/actionTypes’

/** createStore 创建仓库

  • redux 应用中,只能有一个 store,只能有一个 reducer,只能有一个 state;它们都是单例的
  • @param {*} reducer 处理器
  • @param {} initState 仓库的初始状态 / function createStore(reducer, initState){ let listeners = []; //监听函数存储数组 let state = initState; //定义状态变量

    // 获取状态 function getState(){ return state; }

    // 派发action function dispatch(action){ state = reducer(state, action); // 接收reducer,计算新的state listeners.forEach(fn => fn()); //每派发一次动作,执行所有的监听函数 return action; //除了 react ssr 用到了这个返回,其它地方基本用不到 }

    /** 订阅

    • @param {*} listener 监听函数
    • @returns 返回一个函数,用于销毁此监听函数 */ function subscribe(listener){ listeners.push(listener); return () => { listeners.filter(item => item !== listener); } }

    // 初始化state(在创建store的时候,会先派发一次action,让reducer设置的默认值生效) dispatch({ type: ActionTypes.INIT });

    const store = { getState, dispatch, subscribe, } return store; }

export default createStore;

  1. <a name="ALVRW"></a>
  2. ##### src/redux/utils/actionTypes.js
  3. ```javascript
  4. const randomString = () =>
  5. Math.random().toString(36).substring(7).split('').join('.')
  6. const ActionTypes = {
  7. INIT: `@@redux/INIT${randomString()}`,
  8. }
  9. export default ActionTypes

实现 bindActionCreators

  • https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts
    src/redux/bindActionCreators.js
    ```javascript /** bindActionCreator 绑定 action创建者 和 dispatch方法
    • @param {*} actionCreator action创建者
    • @param {} dispatch 派发方法 / function bindActionCreator(actionCreator, dispatch){ return function (…args){ return dispatch(actionCreator.apply(this, args)); } }

/** bindActionCreators 绑定 action创建者们 和 dispatch方法

  • @param {*} actionCreators action创建者们
  • @param {} dispatch 派发方法 / function bindActionCreators(actionCreators, dispatch){ if (typeof actionCreators === ‘function’){ return bindActionCreator(actionCreators, dispatch); }

    if (typeof actionCreators !== ‘object’ || actionCreators === null){ throw new Error(bindActionCreators expected an object or a function .); }

    const boundActionCreators = {}; for (const key in actionCreators){ const actionCreator = actionCreators[key]; if (typeof actionCreator === ‘function’){ boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } return boundActionCreators; }

export default bindActionCreators;

  1. <a name="OCmhZ"></a>
  2. ## 实现 combineReducers
  3. - [https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts](https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts)
  4. <a name="VrQlz"></a>
  5. ##### src/redux/combineReducers.js
  6. ```jsx
  7. /** combineReducers 把一个 reducers 对象变成一个 reducer 函数
  8. *
  9. * @param {*} reducers 样式如: {reducer1: reducer1, reducer2, ...}
  10. */
  11. function combineReducers(reducers){
  12. /** 返回一个函数,就是 根reducers
  13. *
  14. * @param {*} state 根reducers的老状态,样式如:{reducer1: reducer1State, reducer2: reducer2State, ...}
  15. */
  16. function rootReducer(state={}, action){
  17. let nextState = {}; //根reducers的新状态
  18. for (let key in reducers){
  19. const reducer = reducers[key]; //分reducer
  20. const previousStateForKey = state[key]; //分reducer的老状态
  21. const nextStateForKey = reducer(previousStateForKey, action); //分reducer的新状态
  22. nextState[key] = nextStateForKey;
  23. }
  24. return nextState;
  25. }
  26. return rootReducer;
  27. }
  28. export default combineReducers;

实现 compose

实现 applyMiddleware

function applyMiddleware(…middlewares){ return function(createStore){ return function(reducer){ let store = createStore(reducer); //先创建原生的store let dispatch; //指向改造后的dispatch方法 let middlewareAPI = { getState: store.getState, dispatch: action => dispatch(action), }

  1. let chain = middlewares.map(item => item(middlewareAPI));
  2. // let [fn1, fn2, fn3, ...] = chain;
  3. // dispatch = fn1(fn2(fn3(store.dispatch)));
  4. dispatch = compose(...chain)(store.dispatch); //改写dispatch方法
  5. return {...store, dispatch};
  6. }

} } export default applyMiddleware;

// 中间件格式 (以promise中间件格式为例) // function promise(store){ // return function(next){ // return function(action){ // if (typeof action.then === ‘function’){ // return action.then(newAction => store.dispatch(newAction)); // } // return next(action); // } // } // }

  1. <a name="nnpDV"></a>
  2. # react-redux
  3. [https://github.com/reduxjs/react-redux](https://github.com/reduxjs/react-redux)
  4. <a name="FmNxK"></a>
  5. ## 实现 Provider
  6. [https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.js](https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.js)
  7. <a name="HFOCV"></a>
  8. ##### src/react-redux/index.js
  9. ```jsx
  10. export {default as Provider} from './Provider';
  11. export {default as ReactReduxContext} from './ReactReduxContext';

src/react-redux/ReactReduxContext.js
  1. import React from 'react';
  2. const ReactReduxContext = React.createContext();
  3. export default ReactReduxContext;

src/react-redux/Provider.js
  1. import React from 'react';
  2. import ReactReduxContext from './ReactReduxContext';
  3. function Provider(props){
  4. const value = {store: props.store};
  5. // Provider 的下层组件都可以通过 ReactReduxContext 获取value值
  6. return (
  7. <ReactReduxContext.Provider value={value}>
  8. {props.children}
  9. </ReactReduxContext.Provider>
  10. )
  11. }
  12. export default Provider;

实现 connect

https://github.com/reduxjs/react-redux/tree/master/src/connect

  1. import React from 'react';
  2. import ReactReduxContext from './ReactReduxContext';
  3. import {bindActionCreators} from '../redux';
  4. /** connect 把组件和仓库进行关联
  5. *
  6. * @param {*} mapStateToProps 把 仓库状态 映射为属性
  7. * @param {*} mapDispatchToProps 把绑定后的 actions对象 映射为属性
  8. */
  9. function connect(mapStateToProps, mapDispatchToProps){
  10. return function(OldComponent){
  11. return function (props){
  12. const {store} = React.useContext(ReactReduxContext);
  13. const {getState, dispatch, subscribe} = store;
  14. // 映射 state 为属性
  15. const prevState = getState(); //获取仓库中的总状态
  16. let stateProps = mapStateToProps(prevState);
  17. stateProps = React.useMemo(() => stateProps, [stateProps]);
  18. // 映射 绑定后的 actions对象 为属性
  19. const dispatchProps = React.useMemo(() => {
  20. let dispatchProps;
  21. if (typeof mapDispatchToProps === 'object'){
  22. dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
  23. } else if (typeof mapDispatchToProps === 'function'){
  24. dispatchProps = mapDispatchToProps(dispatch);
  25. } else {
  26. dispatchProps = {dispatch};
  27. }
  28. return dispatchProps;
  29. }, [dispatch])
  30. // useReducer解构出来是 [state, dispatch],
  31. // dispatch函数内部处理state的更新逻辑,如果state发生了变化,就会从根组件开始把整个应用更新一下。
  32. // 利用useReducer的这个特性,仓库订阅这个dispatch函数后,仓库每次分发之后,就会更新应用。
  33. const [, forceUpdate] = React.useReducer(x=>x+1, 0);
  34. React.useEffect(() => {
  35. return subscribe(forceUpdate);
  36. }, [subscribe])
  37. return <OldComponent {...props} {...stateProps} {...dispatchProps} />
  38. }
  39. }
  40. }
  41. export default connect;

类组件
  1. // 类组件实现
  2. function connect2(mapStateToProps, actions){
  3. return function(OldComponent){
  4. return class extends React.Component {
  5. static contextType = ReactReduxContext;
  6. componentDidMount(){
  7. this.unsubscribe = this.context.store.subscribe(() => this.forceUpdate());
  8. }
  9. componentWillUnmount(){
  10. this.unsubscribe();
  11. }
  12. render (){
  13. const {store} = this.context;
  14. const prevState = store.getState();
  15. const stateProps = mapStateToProps(prevState);
  16. const dispatchProps = bindActionCreators(actions, store.dispatch);
  17. return <OldComponent {...this.props} {...stateProps} {...dispatchProps} />
  18. }
  19. }
  20. }
  21. }

实现 useDispatch、useSelector、useStore

  • https://github.com/reduxjs/react-redux/tree/master/src/hooks
    src/react-redux/index.js
    1. export {useDispatch, useSelector, useStore} from './hooks';
    src/react-redux/hooks/index.js
    1. export {default as useDispatch} from './useDispatch';
    2. export {default as useSelector} from './useSelector';
    3. export {default as useStore} from './useStore';
    src/react-redux/hooks/useReduxContext.js 【获取redux的上下文】
    ```jsx import React from ‘react’; import ReactReduxContext from ‘../ReactReduxContext’;

export function useReduxContext(){ const contextValue = React.useContext(ReactReduxContext); return contextValue; } export default useReduxContext;

  1. <a name="MfFmD"></a>
  2. ##### src/react-redux/hooks/useStore.js 【获取仓库】
  3. ```jsx
  4. import useReduxContext from './useReduxContext';
  5. function useStore(){
  6. const {store} = useReduxContext();
  7. return store;
  8. }
  9. export default useStore;

src/react-redux/hooks/useDispatch.js 【获取仓库分发(store.dispatch)】
  1. import useReduxContext from './useReduxContext';
  2. function useDispatch(){
  3. const {store} = useReduxContext();
  4. return store.dispatch;
  5. }
  6. export default useDispatch;

src/react-redux/hooks/useSelector.js 【获取状态映射函数(mapStateToProps)】
  1. import React from 'react';
  2. import useReduxContext from './useReduxContext';
  3. function useSelector(selector){
  4. const {store} = useReduxContext();
  5. const {getState, subscribe} = store;
  6. const state = getState(); //获取总状态
  7. const selectedState= selector(state); //获取分状态
  8. // 添加订阅,让组件刷新
  9. const [, forceUpdate] = React.useReducer(x=>x+1, 0);
  10. React.useEffect(() => {
  11. return subscribe(forceUpdate);
  12. }, [subscribe])
  13. return selectedState;
  14. }
  15. export default useSelector;

redux-logger 日志中间件

image.png

  1. // 日志中间件的真正实现(只要是中间件,格式都是定死的)
  2. function logger(store){
  3. return function(next){
  4. // 最终返回 改写后的 dispatch 方法
  5. return function(action){
  6. let styleGrey = 'color: #9E9E9E';
  7. console.group(`%c action %c ${action.type} %c @ ${new Date()}`, styleGrey, 'color: #000', styleGrey);
  8. console.log('%c prev state', styleGrey, store.getState(), );
  9. console.log('%c action', 'color: #03A9F4', action);
  10. next(action);
  11. console.log('%c next state', 'color: #4CAF50', store.getState());
  12. console.groupEnd();
  13. }
  14. }
  15. }
  16. export default logger;

redux-thunk 函数中间件

  • https://github.com/reduxjs/redux-thunk/blob/master/src/index.js ```jsx // 原生的 store.dispatch 只支持传递普通对象,不能接受函数,也不能接收promise。 // 为了要让其支持,添加 支持函数的中间件 function thunk(store){ return function(next){ return function(action){
    1. // 如果派发的action是一个函数,就让它执行
    2. if (typeof action === 'function'){
    3. // 如果这里第一个参数传递next(即原生的store.dispatch方法),
    4. // 那么函数内 dispatch 有可能还是派发的函数。那样的话就报错了。
    5. return action(store.dispatch, store.getState);
    6. }
    7. // 如果不是函数,则意味着不需要自己处理。直接调用下一个dispatch方法即可。
    8. return next(action);
    } } }

export default thunk;

  1. <a name="EBM9t"></a>
  2. # redux-promise Promise中间件
  3. - [https://github.com/redux-utilities/redux-promise/blob/master/src/index.js](https://github.com/redux-utilities/redux-promise/blob/master/src/index.js)
  4. ```jsx
  5. // 添加支持promise的中间件
  6. function promise(store){
  7. return function(next){
  8. return function(action){
  9. if (typeof action.then === 'function'){
  10. return action.then(newAction => store.dispatch(newAction));
  11. }
  12. return next(action);
  13. }
  14. }
  15. }
  16. export default promise;