Redux

1 是什么

Redux 是一个可以管理数据的第三方库,能够协助 React 等界面库对数据进行统一管理。

Redux 并不是必须的,当应用中的数据交互比较简单,使用 React 内部的 state、props、context、ref 等可以轻松完成时,就不必使用 Redux 来对数据进行统一管理。

2 设计核心

  • 唯一数据源
  • 只能通过 Action 触发 Store 的修改
  • Reducer 是纯函数

3 中间件

3.1 Middleware

3.1.1 是什么

在 Redux 中,从形式上讲,Middleware 的格式为 ({ getState, dispatch }) => next => action 的函数。

在 Redux 中,从功能上讲,Middleware 是 dispatch 之后、reducer 之前的扩展点。

Middleware 最优秀的特性就是可以被链式组合,在一个项目中可以使用多个独立的第三方 middleware。

3.1.2 有什么作用

在 Redux 中,通过 Middleware 可以扩展 store.dispatch 功能 ,如 可以进行日志记录、创建崩溃报告、调用异步接口或者路由等等。Middleware 最常用的场景就是异步 action。

3.2 redux-thunk

3.2.1 是什么

thunk 是包裹了表达式的函数,通过函数来包裹表达式,进而达到延迟/异步执行表达式的目的。

redux-thunk 是 Redux 中,用来延迟执行 dispatch 的中间件。

3.2.2 有什么作用

redux-thunk 的作用是使 redux 能够异步 dispatch action。

redux-thunk 使 store.dispatch 可以接收函数类型的 actionCreator(dispatch 默认只支持对象类型的 actionCreator),将 strore.dispatch( actionObj ) 包裹在函数类型的 actionCreator 内部,进而达到**异步 dispatch(action) **的目的。

1
2
3
4
5
6
7
8
9
export const getListDataAction = () => {

// store.dispatch 接收到此函数类型的 action 时,会直接执行它,并给它传递 dispatch 参数。
return (dispatch) => {
axios.get("/api/list.json").then((response) => {
dispatch(initListAction(response.data));
});
};
};

3.2.3 使用方法

  • 安装

    1
    npm install redux-thunk
  • 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //store/index.js
    //基本配置

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from './reducers/index';

    // Note: this API requires redux@>=3.1.0
    const store = createStore(rootReducer, applyMiddleware(thunk));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //store/index.js
    // 和 redux-devtools 一起使用时的配置

    import { createStore, applyMiddleware, compose } from "redux";
    import thunk from "redux-thunk";
    import reducer from "./reducer";

    const composeEnhancers =
    typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
    : compose;

    const enhancer = composeEnhancers(applyMiddleware(thunk));

    const store = createStore(reducer, enhancer);

  • 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // createAction.js 文件

    // getListDataAction 返回的 action 是个函数
    // 这个函数被有一个 dispatch 参数
    export const getListDataAction = () => {
    return (dispatch) => {
    axios.get("/api/list.json").then((response) => {
    dispatch(initListAction(response.data));
    });
    };
    };
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //dispatch 一个函数后 store 会立即执行此函数
    class TodoList extends Component {
    //...
    componentDidMount() {
    //getListDataAction() 返回一个函数
    //当 dispatch 接收到一个函数类型的 action 时,会立即执行返回的函数,并传递 dispatch 参数
    store.dispatch(getListDataAction());
    }
    }

3.2.4 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// redux-thunk 中间件核心代码实现******************************
function createThunkMiddleware(extraArgument) {
return function (_ref) {
var dispatch = _ref.dispatch,
getState = _ref.getState;
return function (next) {
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}

return next(action);
};
};
};
}
var thunk = createThunkMiddleware();// 注意这里执行了一次
thunk.withExtraArgument = createThunkMiddleware;
//***************************************************************

4 API

4.1 createStore

1
createStore(reducer, [preloadedState], enhancer)
  • store.subscribe()
  • store.dispatch(action)
  • store.getState()
1
2
3
4
5
6
7
8
9
10
11
12
13
//createStore 源码中,有 enhancer 参数时的核心代码实现************************
function createStore(reducer, preloadedState, enhancer) {
//...
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.');
}

return enhancer(createStore)(reducer, preloadedState);
}
//...
}
//*****************************************************************

4.2 combineReducers

1
combineReducers(reducers)
  • 可以合并多个 reducer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
    // rootReducer 将返回如下的 state 对象
    {
    potato: {
    // ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ...
    },
    tomato: {
    // ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ...
    }
    }

4.3 applyMiddlleware

1
applyMiddlleware(...middlewares)
  • 参数:

    • middlewares 是由中间件组成的数组,middleware 之间无需相互关心
    • middleware 函数的形式为:({ getState, dispatch }) => next => action
  • 功能描述:

    • 创建一个可以扩展 store 的函数
  • 返回值:

    • 添加了 middleware 的 store ,返回值形式为: createStore => createStore
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
      //applyMiddlleware 源码*********************************
    function applyMiddleware() {
    //将 middlleware 重新放进数组 middlewares 中
    for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
    }

    return function (createStore) {
    return function () {
    var store = createStore.apply(void 0, arguments);

    var _dispatch = function dispatch() {
    throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
    };

    var middlewareAPI = {
    getState: store.getState,
    dispatch: function dispatch() {
    return _dispatch.apply(void 0, arguments);
    }
    };
    var chain = middlewares.map(function (middleware) {
    /* midddleware 的函数形式:
    function (_ref) {
    var dispatch = _ref.dispatch,
    getState = _ref.getState;
    return function (next) {
    return function (action) {
    if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
    }

    return next(action);
    };
    };
    };
    */
    return middleware(middlewareAPI);
    });

    /*chain 数组中的函数形式:
    function (next) {//next 为下一个中间件
    return function (action) {
    if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
    }

    return next(action);
    };
    };
    */
    /*
    执行 compose(f, g, h) 返回值为:
    (...args) => f(g(h(...args))

    故 _dispatch 为 f(g(h(store.dispatch))
    */
    _dispatch = compose.apply(void 0, chain)(store.dispatch);

    return _objectSpread2({}, store, {
    dispatch: _dispatch
    });
    };
    };
    }
    //*******************************************************************
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //示例
    import { createStore, applyMiddleware} from "redux";
    import thunk from "redux-thunk";
    import reducer from "./reducer";

    const enhancer = applyMiddleware(thunk);

    const store = createStore(reducer, enhancer);

    export default store;

4.4 compose

1
compose( ...functions )
  • 参数:

    • 多个需要组合的函数,除了最右边的函数,其余函数都只能接收一个参数,每个函数的返回值都会作为相邻左边函数的参数。
  • 返回值:

    • 返回从右到左依次执行函数参数的组合函数,如 compose(f, g, h) 会返回 (...args) => f(g(h(...args)))
  • 功能描述:

    • 从右到左组合多个函数,使用 compose 来依次执行多个 store enhancer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //redux 中 compose 核心代码实现*********************************
    function compose() {
    for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) {
    funcs[_key] = arguments[_key];
    }
    //.....
    if (funcs.length === 1) {
    return funcs[0];
    }

    return funcs.reduce(function (a, b) {
    return function () {
    return a(b.apply(void 0, arguments));
    };
    });
    }
    //***************************************************************

4.5 bindActionCreator

1
bindActionCreator(actionCreators, dispatch)
  • 参数:

    • actionCreators:如下两种类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      //第一种: 一个 actionsCreator 的函数
      initItemAction = (value) => {
      return {
      type: INIT_ITEM,
      value,
      };
      };

      //第二种:由多个 actionCreator 组成的对象
      import * as TodoActionCreators from './TodoActionCreators';
      console.log(TodoActionCreators);
      // {
      // addTodo: Function,
      // removeTodo: Function
      // }
    • dispatch: Store.dispatch

  • 返回值:

    • actionCreators
  • 功能描述:

    • 修改 actionCreators 中每个 actionCreator 的实现,将其包裹在 dispatch 中。修改后的效果为,执行 actionCreator 创建 action时 ,会同时执行 dispatch(action),使得执行 actionCreator 的返回值为 dispatch(action) 后的结果。

    • 下面例子对比使用 bindActionCreator 前后写法的变化

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import * as TodoActionCreators from './TodoActionCreators';

      //不使用 bindActionCreator 时,创建完 action, 需要手动执行 dispatch
      let action = TodoActionCreators.addTodo('Use Redux');
      dispatch(action);

      //使用 bindActionCreator 把 TodoActionCreators 和 dispatch 绑定后,创建完 action 的同时会在内部自动执行 dispatch
      this.boundActionCreators = bindActionCreators(TodoActionCreators, dispatch);
      boundActionCreators.addTodo('Use Redux');

      1
      2
      3
      4
      5
      6
      7
      //源码************************************************
      function bindActionCreator(actionCreator, dispatch) {
      return function () {
      return dispatch(actionCreator.apply(this, arguments));
      };
      }
      //源码************************************************