关于 Redux 的更多

React-Redux

react-redux 是 Redux 提供的 react 绑定,辅助在 react 项目中使用 redux

它的 API 简单,包括一个组件Provider和一个高阶函数connect

Provider

❓ 为什么Provider只传递一个store,被它包裹的组件都能够访问到store的数据呢?

Provider 做了些啥?

  • 创建一个contextValue包含redux传入的store
  • 通过context上下文把contextValue传递子组件

Connect

❓connect 做了什么事情讷?

使用容器组件通过context提供的store,并将mapStateToPropsmapDispatchToProps返回的statedispatch传递给 UI 组件

组件依赖 redux 的state,映射到容器组件的props中,state改变时触发容器组件的props的改变,触发容器组件组件更新视图

const enhancer = connect(mapStateToProps, mapDispatchToProps);
enhancer(Component);

react-redux 丐版实现

mini-react-redux

Provider

export const Provider = (props) => {
  const { store, children, context } = props;
  const contextValue = { store };
  const Context = context || ReactReduxContext;
  return <Context.Provider value={contextValue}>{children}</Context.Provider>;
};

connect

import { useContext, useReducer } from 'react';
import { ReactReduxContext } from './ReactReduxContext';

export const connect =
  (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => (props) => {
    const { ...wrapperProps } = props;
    const context = useContext(ReactReduxContext);
    const { store } = context; // 解构出 store
    const state = store.getState(); // 拿到 state
    //使用 useReducer 得到一个强制更新函数
    const [, forceComponentUpdateDispatch] = useReducer(
      (count) => count + 1,
      0,
    );
    // 订阅 state 的变化,当 state 变化的时候执行回调
    store.subscribe(() => {
      forceComponentUpdateDispatch();
    });
    // 执行 mapStateToProps 和 mapDispatchToProps
    const stateProps = mapStateToProps?.(state);
    const dispatchProps = mapDispatchToProps?.(store.dispatch);
    // 组装最终的 props
    const actualChildProps = Object.assign(
      {},
      stateProps,
      dispatchProps,
      wrapperProps,
    );
    return <WrappedComponent {...actualChildProps} />;
  };

Redux Middleware

middleware提供分类处理action的机会,在middleware中可以检查每一个action,挑选出特定类型的action做对应操作

image

middleware 示例

打印日志

store.dispatch = (action) => {
  console.log('this state', store.getState());
  console.log(action);
  next(action);
  console.log('next state', store.getState());
};

监控错误

store.dispatch = (action) => {
  try {
    next(action);
  } catch (err) {
    console.log('catch---', err);
  }
};

二者合二为一

store.dispatch = (action) => {
  try {
    console.log('this state', store.getState());
    console.log(action);
    next(action);
    console.log('next state', store.getState());
  } catch (err) {
    console.log('catch---', err);
  }
};

提取 loggerMiddleware/catchMiddleware

const loggerMiddleware = (action) => {
  console.log('this state', store.getState());
  console.log('action', action);
  next(action);
  console.log('next state', store.getState());
};
const catchMiddleware = (action) => {
  try {
    loggerMiddleware(action);
  } catch (err) {
    console.error('错误报告: ', err);
  }
};
store.dispatch = catchMiddleware;

catchMiddleware 中都写死了调用的 loggerMiddleware,loggerMiddleware 中写死了 next(store.dispatch),需要灵活运用,让 middleware 接受 dispatch 参数

const loggerMiddleware = (next) => (action) => {
  console.log('this state', store.getState());
  console.log('action', action);
  next(action);
  console.log('next state', store.getState());
};
const catchMiddleware = (next) => (action) => {
  try {
    /*loggerMiddleware(action);*/
    next(action);
  } catch (err) {
    console.error('错误报告: ', err);
  }
};
/*loggerMiddleware 变成参数传进去*/
store.dispatch = catchMiddleware(loggerMiddleware(next));

middleware 中接受一个 store,就能够把上面的方法提取到单独的函数文件中

export const catchMiddleware = (store) => (next) => (action) => {
  try {
    next(action);
  } catch (err) {
    console.error('错误报告: ', err);
  }
};

export const loggerMiddleware = (store) => (next) => (action) => {
  console.log('this state', store.getState());
  console.log('action', action);
  next(action);
  console.log('next state', store.getState());
};

const logger = loggerMiddleware(store);
const exception = catchMiddleware(store);
store.dispatch = exception(logger(next));

每个 middleware 都需要接受 store 参数,继续优化这个调用函数

export const applyMiddleware =
  (middlewares) => (oldCreateStore) => (reducer, initState) => {
    // 获得老的store
    const store = oldCreateStore(reducer, initState);
    // [catch, logger]
    const chain = middlewares.map((middleware) => middleware(store));
    let oldDispatch = store.dispatch;
    chain
      .reverse()
      .forEach((middleware) => (oldDispatch = middleware(oldDispatch)));
    store.dispatch = oldDispatch;
    return store;
  };

const newStore = applyMiddleware([catchMiddleware, loggerMiddleware])(
  createStore,
)(rootReducer);

Redux 提供了applyMiddleware来加载middleware,applyMiddleware接受三个参数,middlewares数组/reduxcreateStore/reducer

export default const applyMiddleware = (...middlewares) => createStore => (reducer, ...args) => {
  //由createStore和reducer创建store
    const store = createStore(reducer, ...args)
    let dispatch = store.dispatch
    var middlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args)
    }
    // 把 getState/dispatch 传给 middleware,
    // map 让每个 middleware 获得了 middlewareAPI 参数
    // 形成一个 chain 匿名函数数组 [f1,f2,f3...fn]
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // dispatch = f1(f2(f3(store.dispatch))),把所有的 middleware 串联起来
    dispatch = compose(...chain)(store.dispatch)
    return {
        ...store,
        dispatch
    }
}

applyMiddleware 符合洋葱模型

image

参考链接