Redux Thunk ============= Thunk [middleware](https://redux.js.org/advanced/middleware) for Redux. [![build status](https://img.shields.io/travis/reduxjs/redux-thunk/master.svg?style=flat-square)](https://travis-ci.org/reduxjs/redux-thunk) [![npm version](https://img.shields.io/npm/v/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk) [![npm downloads](https://img.shields.io/npm/dm/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk) ```js npm install --save redux-thunk ``` ## Note on 2.x Update Most tutorials today assume Redux Thunk 1.x so you might run into an issue when running their code with 2.x. **If you use Redux Thunk 2.x in CommonJS environment, [don’t forget to add `.default` to your import](https://github.com/reduxjs/redux-thunk/releases/tag/v2.0.0):** ```diff - var ReduxThunk = require('redux-thunk') + var ReduxThunk = require('redux-thunk').default ``` If you used ES modules, you’re already all good: ```js import ReduxThunk from 'redux-thunk' // no changes here 😀 ``` Additionally, since 2.x, we also support a [UMD build](https://unpkg.com/redux-thunk/dist/redux-thunk.min.js): ```js var ReduxThunk = window.ReduxThunk.default ``` As you can see, it also requires `.default` at the end. ## Why Do I Need This? If you’re not sure whether you need it, you probably don’t. **[Read this for an in-depth introduction to thunks in Redux.](http://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559)** ## Motivation Redux Thunk [middleware](https://github.com/reactjs/redux/blob/master/docs/advanced/Middleware.md) 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: ```js const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; function increment() { return { type: INCREMENT_COUNTER }; } function incrementAsync() { return dispatch => { setTimeout(() => { // Yay! Can invoke sync or async actions with `dispatch` dispatch(increment()); }, 1000); }; } ``` An action creator that returns a function to perform conditional dispatch: ```js function incrementIfOdd() { return (dispatch, getState) => { const { counter } = getState(); if (counter % 2 === 0) { return; } dispatch(increment()); }; } ``` ## What’s a thunk?! A [thunk](https://en.wikipedia.org/wiki/Thunk) is a function that wraps an expression to delay its evaluation. ```js // calculation of 1 + 2 is immediate // x === 3 let x = 1 + 2; // calculation of 1 + 2 is delayed // foo can be called later to perform the calculation // foo is a thunk! let foo = () => 1 + 2; ``` The term [originated](https://en.wikipedia.org/wiki/Thunk#cite_note-1) as a humorous past-tense version of "think". ## Installation ``` npm install --save redux-thunk ``` Then, to enable Redux Thunk, use [`applyMiddleware()`](https://redux.js.org/api-reference/applymiddleware): ```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) ); ``` ## 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: ```js import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducers'; // Note: this API requires redux@>=3.1.0 const store = createStore( rootReducer, applyMiddleware(thunk) ); function fetchSecretSauce() { return fetch('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”. function makeASandwich(forPerson, secretSauce) { return { type: 'MAKE_SANDWICH', forPerson, secretSauce }; } function apologize(fromPerson, toPerson, error) { return { type: 'APOLOGIZE', fromPerson, toPerson, error }; } function withdrawMoney(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. function makeASandwichWithSecretSauce(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. return function (dispatch) { return fetchSecretSauce().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. function makeSandwichesForEverybody() { return function (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. return Promise.resolve(); } // We can dispatch both plain object actions and other thunks, // which lets us compose the asynchronous actions in a single flow. return dispatch( 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()) ); // 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'; class SandwichShop extends Component { 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

{this.props.sandwiches.join('mustard')}

} } export default connect( state => ({ sandwiches: state.sandwiches }) )(SandwichShop); ``` ## Injecting a Custom Argument Since 2.1.0, Redux Thunk supports injecting a custom argument using the `withExtraArgument` function: ```js const store = createStore( reducer, applyMiddleware(thunk.withExtraArgument(api)) ) // later function fetchUser(id) { return (dispatch, getState, api) => { // you can use api here } } ``` To pass multiple things, just wrap them in a single object and use destructuring: ```js const store = createStore( reducer, applyMiddleware(thunk.withExtraArgument({ api, whatever })) ) // later function fetchUser(id) { return (dispatch, getState, { api, whatever }) => { // you can use api and something else here } } ``` ## License MIT