进击的Redux

网上Redux入门教程很多,但是为啥这么使用,却百思不得解。今天写一篇关于Redux的文章,帮助大家了解Redux为啥这么写,以及内部做了啥。

目录:

  • types
    • actions.ts
    • middleware.ts
    • reducers.ts
    • store.ts
  • utils
    • actionTypes.ts
    • formatProdErrorMessage.ts
    • isPlainObject.ts
    • kindOf.ts
    • symbol-observable.ts
    • warning.ts
  • applyMiddleware.ts
  • bindActionCreators.ts
  • combineReducers.ts
  • compose.ts
  • createStore.ts
  • index.ts

applyMiddleware.ts

将所有中间件合并成一个数组,然后依次执行,用法如下:

const store = applyMiddleware([ loggerMiddleware, thunkMiddleware, ...others ])(createStore)(reducer, preloadState)
复制代码

源码如下:

export default function applyMiddleware (...middlewares) {
  return (createStore) => (reducer, preloadState) => {
    const store = createStore(reducer, preloadState)
    let dispatch = () => {}
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // compose 是 src 目录下的compose方法,暂时不用管
    dispatch = compose(...chain)(store.dispatch)
    return {
       ...store,
       dispatch
    }
  }
}
复制代码

bindActionCreators.ts

将一个或多个action和dispatch组合起来生成mapDispatchToProps需要生成的内容,目的就是简化书写,减轻开发负担。用法如下:

// 使用前
import React, { useCallback } from "react";
import { createStore, bindActionCreators } from "redux";
import { Provider, connect } from "react-redux";

// 子组件
function Child({ msg, onClick }) {
  const onUserClick = useCallback(() => {
    onClick("我被点击了");
  }, [onClick]);
  return <div onClick={onUserClick}>被点击了吗? {msg}</div>;
}

// 让子组件使用redux
const ChildWrap = connect(
  state => ({ msg: state.msg }),
  dispatch => ({
    onClick: payload => dispatch({ type: "CLICK", payload })
  })
)(React.memo(Child));

export default function App() {
  const store = createStore((state = {}, action) => {
    switch (action.type) {
      case "CLICK":
        state = { msg: action.payload };
        break;
      default:
        state = {};
        break;
    }
    return state;
  });
  return (
    <Provider store={store}>
      <div className="App">
        <ChildWrap />
      </div>
    </Provider>
  );
}

// 使用后
import * as actionCreators from './actionCreators'

const ChildWrap = connect(
  (state) => ({ msg: state.msg }),
  (dispatch, ownProps) => bindActionCreators(actionCreators, dispatch)
)(React.memo(Child));

// actionCreators.js
export const onClick = (payload) => ({
  type: "CLICK",
  payload
});
复制代码

源码如下:

/**
 * 批量处理action
 * @actionCreators { Function | Object } 如果是多个action,则需要传入对象
 * @dispatch       { Function }          store的dispatch对象
 */
export default function bindActionCreators (actionCreators, dispatch) {
  // 如果是单个函数
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'funciton') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

/**
 * 处理单个action
 * @param actionCreators { Function | Object } 如果是多个action,则需要传入对象
 * @param dispatch       { Function }          store的dispatch对象
 */
export default function bindActionCreator (actionCreator, dispatch) {
  return function (this, ...args) {
    return dispatch(actionCreator.bind(this, ...args))
  }
}
复制代码

combineReducers.ts

使reducer结合到一起。即将各个子reducer合并成一个大的reducer合并后的reducer可以调用各个子 reducer,并把它们返回的结果合并成一个state对象。
参数: 一个Object对象,key为 reducerName 可以自定义,value为 reducer函数
返回值: 调用所有传入的reducer,即传入参数对象的 value 值。返回和传入参数结构一致的state对象

import React, { useCallback } from "react";
import { createStore, combineReducers } from "redux";
import { Provider, connect } from "react-redux";

function Child(props) {
  const { msg, onClick } = props
  const onUserClick = useCallback(() => {
    onClick("sssss");
  }, [onClick]);
  return <div onClick={onUserClick}>11111 {msg}</div>;
}

const ChildWrap = connect(
  // 注意此处的state.reduce1对应combineReducer中写的key
  (state) => ({ msg: state.reducer1.msg }),
  (dispatch) => ({
    onClick: (payload) => dispatch({ type: "CLICK", payload })
  })
)(Child);

export default function App() {
  const reducer1 = (state = {}, action) => {
    switch (action.type) {
      case "CLICK":
        state = { msg: action.payload };
        break;
      default:
        state = {};
        break;
    }
    return state;
  };

  const reducer2 = (state = {}, action) => {
    switch (action.type) {
      case "CLICK2":
        state = { msg: action.payload };
        break;
      default:
        state = {};
        break;
    }
    return state;
  };

  const store = createStore(combineReducers({ reducer1, reducer2 }));

  return (
    <Provider store={store}>
      <div className="App">
        <ChildWrap />
      </div>
    </Provider>
  );
}
复制代码

源码如下:

export default function combineReducers (reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  // 剔除reducer中非方法的内容
  for (let i = 0; i < reducerKeys.length; i ++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)
  // 组装在一起,相当于一个大的reducer,入参也和reducer一致
  return function combination (state = {}, action) {
    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i ++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducerKeys[key]
      const previousStateForKey = state[key]
      let nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}
复制代码

compose.ts

函数式编程里常用的组合函数,将多个函数组合成一个函数依次执行。个人认为这个函数是redux整个源码中最值得一看的代码,而且可以脱离redux中,在日常开发中使用。

export default function compose (...funcs) {
  if (funcs.length === 0) {
    return () => {}
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码

createStore.ts

相信这个方法绝对是大家用redux最多的一个方法,直接上源码

/**
 * 创建store的方法
 *
 * @param reducer        纯函数,接收store和action,返回新store
 * @param preloadedState 初始化的state值
 * @param enhancer       store增强函数,类似于applyMiddleware
 */
export default function createStore (reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = null
  }
  if (typeof enhancer !== 'undefined' && typeof enhancer === 'function') {
    return enhancer(createStore)(reducer, preloadedState)
  }

  let currentState = preloadedState
  let currentReducer = reducer
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  /**
   * 生成currentListeners的浅层副本,使用时可以将nextListeners用作临时列表。
   */
  function ensureCanMutateNextListeners () {
    if (currentListeners === nextListeners) {
      nextListeners = currentListeners.splice()
    }
  }

  /**
   * 获取最新的状态
   */
  function getState () {
    return currentState
  }

  /**
   * 添加更改侦听器。它将在任何时候调用一个动作,并且状态树的某些部分可能已经更改。然后可以调
   * 用“getState()”来读取回调中的当前状态树。
   */
  function subscribe (listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)
    
    return function unSubscribe () {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

  /**
   * 派发事件,触发reducer改变state的值。这是唯一可以修改state值的方法
   */
  function dispatch (action) {
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
      )
    }
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i ++) {
      const listener = listeners[i]
      listener()
    }
    return action
  }

  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer: unknown) {
        function observeState() {
          const observerAsObserver = observer as Observer<S>
          if (observerAsObserver.next) {
            observerAsObserver.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  /**
   * 替换当前当前用于计算的reducer
   */
  function replaceReducer (nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
    return store
  }

  // 所以明白了,reducer中的switch的default开始会执行一次了吧
  dispatch({ type: ActionTypes.INIT })

  const store = {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
  return store
}
复制代码

index.ts

导出上面的方法

types

定义了整个项目中需要使用的interface和type,篇幅原因,不再详细说明,有兴趣可以查看源码

utils

工具类方法,篇幅原因,不再详细说明,有兴趣可以查看源码

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享