Why Do I Need This?

Motivation

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

An action creator that returns a function to perform asynchronous dispatch:

Composition

Any return value from the inner function will be available as the return value of dispatch itself. This is convenient for orchestrating an asynchronous control flow with thunk action creators dispatching each other and returning Promises to wait for each other’s completion:

import { createStore, applyMiddleware } from'redux';
importthunkfrom'redux-thunk';
importrootReducerfrom'./reducers';
// Note: this API requires redux@>=3.1.0conststore=createStore(
rootReducer,
applyMiddleware(thunk)
);
functionfetchSecretSauce() {
returnfetch('https://www.google.com/search?q=secret+sauce');
}
// These are the normal action creators you have seen so far.// The actions they return can be dispatched without any middleware.// However, they only express “facts” and not the “async flow”.functionmakeASandwich(forPerson, secretSauce) {
return {
type:'MAKE_SANDWICH',
forPerson,
secretSauce
};
}
functionapologize(fromPerson, toPerson, error) {
return {
type:'APOLOGIZE',
fromPerson,
toPerson,
error
};
}
functionwithdrawMoney(amount) {
return {
type:'WITHDRAW',
amount
};
}
// Even without middleware, you can dispatch an action:store.dispatch(withdrawMoney(100));
// But what do you do when you need to start an asynchronous action,// such as an API call, or a router transition?// Meet thunks.// A thunk is a function that returns a function.// This is a thunk.functionmakeASandwichWithSecretSauce(forPerson) {
// Invert control!// Return a function that accepts `dispatch` so we can dispatch later.// Thunk middleware knows how to turn thunk async actions into actions.returnfunction (dispatch) {
returnfetchSecretSauce().then(
sauce=>dispatch(makeASandwich(forPerson, sauce)),
error=>dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
// Thunk middleware lets me dispatch thunk async actions// as if they were actions!store.dispatch(
makeASandwichWithSecretSauce('Me')
);
// It even takes care to return the thunk’s return value// from the dispatch, so I can chain Promises as long as I return them.store.dispatch(
makeASandwichWithSecretSauce('My wife')
).then(() => {
console.log('Done!');
});
// In fact I can write action creators that dispatch// actions and async actions from other action creators,// and I can build my control flow with Promises.functionmakeSandwichesForEverybody() {
returnfunction (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
// You don’t have to return Promises, but it’s a handy convention// so the caller can always call .then() on async dispatch result.returnPromise.resolve();
}
// We can dispatch both plain object actions and other thunks,// which lets us compose the asynchronous actions in a single flow.returndispatch(
makeASandwichWithSecretSauce('My Grandma')
).then(() =>Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
dispatch(makeASandwichWithSecretSauce('My wife'))
])
).then(() =>dispatch(makeASandwichWithSecretSauce('Our kids'))
).then(() =>dispatch(getState().myMoney>42?withdrawMoney(42) :apologize('Me', 'The Sandwich Shop')
)
);
};
}
// This is very useful for server side rendering, because I can wait// until data is available, then synchronously render the app.store.dispatch(
makeSandwichesForEverybody()
).then(() =>response.send(ReactDOMServer.renderToString(<MyApp store={store} />))
);
// I can also dispatch a thunk async action from a component// any time its props change to load the missing data.import { connect } from'react-redux';
import { Component } from'react';
classSandwichShopextendsComponent {
componentDidMount() {
this.props.dispatch(
makeASandwichWithSecretSauce(this.props.forPerson)
);
}
componentDidUpdate(prevProps) {
if (prevProps.forPerson!==this.props.forPerson) {
this.props.dispatch(
makeASandwichWithSecretSauce(this.props.forPerson)
);
}
}
render() {
return<p>{this.props.sandwiches.join('mustard')}</p>
}
}
exportdefaultconnect(
state=> ({
sandwiches:state.sandwiches
})
)(SandwichShop);

Injecting a Custom Argument

Since 2.1.0, Redux Thunk supports injecting a custom argument using the withExtraArgument function: