Redux

216 阅读4分钟

核心概念

  • Store
    • 是保存数据的地方,可以把它看成一个容器。整个应用只能有一个Store
    • getState()方法获取State
    • dispatch(action)方法,触发reducers方法更新State
    • subscribe(listener)注册监听器
    • 通过subscribe(listener)返回的注销监听器的函数,注销监听器
  • State
    • Store包含所有数据,如果想得到某个时点的数据,就要对应Store的快照,这种时间点的数据集合就叫State
    • 一个State对应一个View,只要State相同,View就相同
  • Action
    • 描述当前发生了什么的普通对象,是改变State的唯一方法
    • type属性是必须的,表示Action的名称
    • 异步 Action
      • 由于 Reducer 是一个严格的纯函数,因此无法在 Reducer 中进行数据的请求,需要先获取数据,再dispatch(Action)即可
      • redex-thunk
      • redux-saga
      • redux-observable
    • Action创建函数
      • Action创建函数就是生成Action的方法
  • Reducer
    • 描述了应用如何更新State
    • store.dispatch方法会出发Reducer的自动执行
    • 是一个纯函数,它接受Action和当前State作为参数,返回新State
    • 拆分Reducer
      • 每个Reducer只负责管理全局State中它负责的一部
      • combineReducers()进行重构合并
  • React-Redux
    • <Provider>: 将 store 通过 context 传入组件中
    • connect(): 一个高阶组件,可以方便在 React 组件中使用 Redux
      • 将 store 通过 mapStateToProps 进行筛选后使用 props 注入组件
      • 根据 mapDispatchToProps 创建方法,当组件调用时使用 dispatch 触发对应的action

数据生命周期

  • 调用store.dispatch(action)
    • action是描述发生了什么的普通对象
    • 可以在任何地方调用store.dispatch(action),包括组件中、XHR回调中、甚至是定时器
  • Store调用创建时传入的Reducer函数
    • store会把两个参数传入reducer函数,当前的state和action
  • Reducer 把多个子reducer输出合并成一个单一的state树
    • Redux提供combineReducers()辅助函数,来把根Reducer拆分成多个函数,用于分别处理state树的一个分支
  • Store保存根Reducer返回的完整State树,并调用监听器

中间件

中间件的概念

中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

中间件的用法

createStore() 方法可以接受整个应用的初始状态作为参数,applyMiddleware 是第三个参数

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(thunk, promise, logger)
);

applyMiddlewares()

applyMiddlewares() 是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行

所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法。

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {...store, dispatch}
  }
}

redux-thunk 中间件

异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch使其可以接受函数作为参数

  1. 组件加载成功后(componentDidMount方法),送出了(dispatch方法)一个 Action,向服务器请求数据 fetchPosts(selectedSubreddit)。

  2. fetchPosts就是 Action Creator(动作生成器),返回一个函数

    • fetchPosts 返回了一个函数,而普通的 Action Creator 默认返回一个对象
    • 返回的函数的参数是 dispatch 和 getState 这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容
    • 在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始
    • 异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束
  3. Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。这时,就要使用中间件redux-thunk 改造store.dispatch,使得后者可以接受函数作为参数。

class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    store.dispatch(fetchPosts(selectedPost))
}
const fetchPosts = postTitle => (dispatch, getState) => {
  dispatch(requestPosts(postTitle));
  return fetch(`/some/API/${postTitle}.json`)
    .then(response => response.json())
    .then(json => dispatch(receivePosts(postTitle, json)));
  };
};
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';

// Note: this API requires redux@>=3.1.0
const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

redux-promise 中间件

异步操作的第一种解决方案就是,让 Action Creator 返回一个 Promise 对象,需要使用redux-promise中间件

import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';

const store = createStore(
  reducer,
  applyMiddleware(promiseMiddleware)
); 
// Action Creator 写法一,返回值是一个 Promise 对象
const fetchPosts = 
  (dispatch, postTitle) => new Promise(function (resolve, reject) {
    dispatch(requestPosts(postTitle));
    return fetch(`/some/API/${postTitle}.json`)
        .then(response => {
            type: 'FETCH_POSTS',
            payload: response.json()
       });
});
// Action Creator 写法二,Action 对象的payload属性是一个 Promise 对象。需要从redux-actions模块引入createAction方法
import { createAction } from 'redux-actions';

class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    // 发出同步 Action
    dispatch(requestPosts(selectedPost));
    // 发出异步 Action
    dispatch(createAction(
      'FETCH_POSTS', 
      fetch(`/some/API/${postTitle}.json`)
        .then(response => response.json())
    ));
  }

redux-promise的源码如下。如果 Action 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    return isPromise(action.payload)
      ? action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          }
        )
      : next(action);
  };
}