Redux-thunk是一个redux的middleware,用来支持异步dispatch。

版本:v2.3.0

动机

完整的动机说明可以直接看Dan Abramov在Stackoverflow的回答。

很详细的解释了为什么需要Redux-thunk

下面我自己简单的总结一下:

假如我们有一个异步dispatch action的需求,正常我们会这样写:

function App() {
  function asyncDispatch() {
    dispatch({ type: 'value', payload: '立即更新的逻辑' });

    setTimeout(() => {
      dispatch({ type: 'value', payload: '延时更新的逻辑' });
    }, 3000);
  }

  return (
    <div>
      <button onClick={() => asyncDispatch()}></button>
    </div>
  )
}

这样写是没有任何问题的,但是如果这个逻辑是复用的,需要再别的组件里面使用,我们就需要这样封装。

function asyncDispatch(dispatch) {
  dispatch({ type: 'value', payload: '立即更新的逻辑' });

  setTimeout(() => {
    dispatch({ type: 'value', payload: '延时更新的逻辑' });
  }, 3000);
}

调用的时候要这样调用:

asyncDispatch(dispatch);

这样写也是完全ok的。但是问题来了,首先我们需要传一个dispatch传过去,这样一来所有的异步操作都要接收一个dispatch过去,然后在connect()中也无法在生成一个action creators,因为asyncDispatch是一个函数且没有返回Redux action。而且,我们也要区分哪些action是同步的,哪些是异步的。

Redux thunk就是为了解决上面的问题而出现的,看下在Redux thunk中上面的逻辑是怎么写的:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(
  reducer,
  applyMiddleware(thunk)
)
function asyncDispatch(text) {
  return function (dispatch) {
    dispatch({ type: 'value', payload: text });

    setTimeout(() => {
      dispatch({ type: 'value', payload: '延时更新的逻辑' });
    }, 3000);
  }
}

然后,我们调用的时候只需:

this.props.dispatch(asyncDispatch('立即更新的逻辑'));

connect()中也可以这样写:

export default connect(
  mapStateToProps,
  { asyncDispatch }
)(MyComponent)

其实,Redux-thunk就是赋予了Redux的dispatch接收函数的能力。然后这个函数会给出两个参数dispatchgetState

所以说,说明白点Redux thunk出现的原因就是Redux的dispatch只接收plain object。而这,也是Redux故意为之,让外部可以自由扩展。

用法

Redux-thunk的用法非常简单。

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(
  reducer,
  applyMiddleware(thunk)
)

这样就赋予了dispatch能接收函数的能力。使用的时候:

function asyncDispatch(text) {
  return function (dispatch, getState, otherArgument) {
    dispatch({ type: 'value', payload: text });
  }
}

this.props.dispatch(asyncDispatch('xx'));
  • dispatch:redux的dispatch
  • getState:redux的getState
  • otherArgument:自定义的其他参数,要使用的话,生成middleware的时候要这样使用
const api = "http://www.example.com/sandwiches/";
const whatever = 42;

const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument({ api, whatever })),
);

然后使用的时候,就会出现在第三个参数里面。

function fetchUser(id) {
  return (dispatch, getState, { api, whatever }) => {
    // you can use api and something else here
  };
}