diff --git a/README.md b/README.md
index 1d4d7f4..1d8b6b2 100644
--- a/README.md
+++ b/README.md
@@ -1,43 +1,147 @@
# Redux Thunk
-Thunk [middleware](https://redux.js.org/advanced/middleware) for Redux.
+Thunk [middleware](https://redux.js.org/tutorials/fundamentals/part-4-store#middleware) for Redux. It allows writing functions with logic inside that can interact with a Redux store's `dispatch` and `getState` methods.
+
+For complete usage instructions and useful patterns, see the [Redux docs **Writing Logic with Thunks** page](https://redux.js.org/usage/writing-logic-thunks).
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/reduxjs/redux-thunk/Tests)
[![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)
+## Installation and Setup
+
+### Redux Toolkit
+
+If you're using [our official Redux Toolkit package](https://redux-toolkit.js.org) as recommended, there's nothing to install - RTK's `configureStore` API already adds the thunk middleware by default:
+
+```js
+import { configureStore } from '@reduxjs/toolkit'
+
+import todosReducer from './features/todos/todosSlice'
+import filtersReducer from './features/filters/filtersSlice'
+
+const store = configureStore({
+ reducer: {
+ todos: todosReducer,
+ filters: filtersReducer
+ }
+})
+
+// The thunk middleware was automatically added
+```
+
+### Manual Setup
+
+If you're using the basic Redux `createStore` API and need to set this up manually, first add the `redux-thunk` package:
+
```sh
npm install redux-thunk
yarn add redux-thunk
```
-## Note on 2.x Update
+The thunk middleware is the default export.
-Most tutorials today assume that you're using Redux Thunk 1.x. You may run into
-issues when you run 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):**
+
+More Details: Importing the thunk middleware
-```diff
-- const ReduxThunk = require('redux-thunk')
-+ const ReduxThunk = require('redux-thunk').default
+If you're using ES modules:
+
+```js
+import thunk from 'redux-thunk' // no changes here 😀
```
-If you used ES modules, you’re already all good:
+If you use Redux Thunk 2.x in a CommonJS environment,
+[don’t forget to add `.default` to your import](https://github.com/reduxjs/redux-thunk/releases/tag/v2.0.0):
-```js
-import ReduxThunk from 'redux-thunk'; // no changes here 😀
+```diff
+- const thunk = require('redux-thunk')
++ const thunk = require('redux-thunk').default
```
Additionally, since 2.x, we also support a
[UMD build](https://unpkg.com/redux-thunk/dist/redux-thunk.min.js):
```js
-const ReduxThunk = window.ReduxThunk.default;
+const ReduxThunk = window.ReduxThunk
```
-As you can see, it also requires `.default` at the end.
+
+
+Then, to enable Redux Thunk, use
+[`applyMiddleware()`](https://redux.js.org/api/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))
+```
+
+### Injecting a Custom Argument
+
+Since 2.1.0, Redux Thunk supports injecting a custom argument into the thunk middleware. This is typically useful for cases like using an API service layer that could be swapped out for a mock service in tests.
+
+For Redux Toolkit, the `getDefaultMiddleware` callback inside of `configureStore` lets you pass in a custom `extraArgument`:
+
+```js
+import { configureStore } from '@reduxjs/toolkit'
+import rootReducer from './reducer'
+import { myCustomApiService } from './api'
+
+const store = configureStore({
+ reducer: rootReducer,
+ middleware: getDefaultMiddleware =>
+ getDefaultMiddleware({
+ thunk: {
+ extraArgument: myCustomApiService
+ }
+ })
+})
+
+// later
+function fetchUser(id) {
+ // The `extraArgument` is the third arg for thunk functions
+ return (dispatch, getState, api) => {
+ // you can use api here
+ }
+}
+```
+
+If you need to pass in multiple values, combine them into a single object:
+
+```js
+const store = configureStore({
+ reducer: rootReducer,
+ middleware: getDefaultMiddleware =>
+ getDefaultMiddleware({
+ thunk: {
+ extraArgument: {
+ api: myCustomApiService,
+ otherValue: 42
+ }
+ }
+ })
+})
+
+// later
+function fetchUser(id) {
+ return (dispatch, getState, { api, otherValue }) => {
+ // you can use api and something else here
+ }
+}
+```
+
+If you're setting up the store by hand, the default `thunk` export has an attached `thunk.withExtraArgument()` function that should be used to generate the correct thunk middleware:
+
+```js
+const store = createStore(
+ reducer,
+ applyMiddleware(thunk.withExtraArgument(api))
+)
+```
## Why Do I Need This?
@@ -51,6 +155,10 @@ async logic like AJAX requests.
For more details on why thunks are useful, see:
+- **Redux docs: Writing Logic with Thunks**
+ https://redux.js.org/usage/writing-logic-thunks
+ The official usage guide page on thunks. Covers why they exist, how the thunk middleware works, and uesful patterns for using thunks.
+
- **Stack Overflow: Dispatching Redux Actions with a Timeout**
http://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559
Dan Abramov explains the basics of managing async behavior in Redux, walking
@@ -80,7 +188,7 @@ it is used by default in our
## Motivation
-Redux Thunk [middleware](https://redux.js.org/advanced/middleware)
+Redux Thunk [middleware](https://redux.js.org/tutorials/fundamentals/part-4-store#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
@@ -89,21 +197,21 @@ a certain condition is met. The inner function receives the store methods
An action creator that returns a function to perform asynchronous dispatch:
```js
-const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
+const INCREMENT_COUNTER = 'INCREMENT_COUNTER'
function increment() {
return {
- type: INCREMENT_COUNTER,
- };
+ type: INCREMENT_COUNTER
+ }
}
function incrementAsync() {
- return (dispatch) => {
+ return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
- dispatch(increment());
- }, 1000);
- };
+ dispatch(increment())
+ }, 1000)
+ }
}
```
@@ -112,14 +220,14 @@ An action creator that returns a function to perform conditional dispatch:
```js
function incrementIfOdd() {
return (dispatch, getState) => {
- const { counter } = getState();
+ const { counter } = getState()
if (counter % 2 === 0) {
- return;
+ return
}
- dispatch(increment());
- };
+ dispatch(increment())
+ }
}
```
@@ -131,35 +239,17 @@ expression to delay its evaluation.
```js
// calculation of 1 + 2 is immediate
// x === 3
-let x = 1 + 2;
+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;
+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
-
-```bash
-npm install redux-thunk
-```
-
-Then, to enable Redux Thunk, use
-[`applyMiddleware()`](https://redux.js.org/api/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
@@ -168,15 +258,15 @@ 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';
+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));
+const store = createStore(rootReducer, applyMiddleware(thunk))
function fetchSecretSauce() {
- return fetch('https://www.google.com/search?q=secret+sauce');
+ return fetch('https://www.google.com/search?q=secret+sauce')
}
// These are the normal action creators you have seen so far.
@@ -187,8 +277,8 @@ function makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
- secretSauce,
- };
+ secretSauce
+ }
}
function apologize(fromPerson, toPerson, error) {
@@ -196,19 +286,19 @@ function apologize(fromPerson, toPerson, error) {
type: 'APOLOGIZE',
fromPerson,
toPerson,
- error,
- };
+ error
+ }
}
function withdrawMoney(amount) {
return {
type: 'WITHDRAW',
- amount,
- };
+ amount
+ }
}
// Even without middleware, you can dispatch an action:
-store.dispatch(withdrawMoney(100));
+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?
@@ -222,37 +312,37 @@ function makeASandwichWithSecretSauce(forPerson) {
// When this function is passed to `dispatch`, the thunk middleware will intercept it,
// and call it with `dispatch` and `getState` as arguments.
// This gives the thunk function the ability to run some logic, and still interact with the store.
- return function(dispatch) {
+ return function (dispatch) {
return fetchSecretSauce().then(
- (sauce) => dispatch(makeASandwich(forPerson, sauce)),
- (error) => dispatch(apologize('The Sandwich Shop', forPerson, error)),
- );
- };
+ 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'));
+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 partner')).then(() => {
- console.log('Done!');
-});
+ 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) {
+ 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();
+ return Promise.resolve()
}
// We can dispatch both plain object actions and other thunks,
@@ -262,18 +352,18 @@ function makeSandwichesForEverybody() {
.then(() =>
Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
- dispatch(makeASandwichWithSecretSauce('My wife')),
- ]),
+ dispatch(makeASandwichWithSecretSauce('My wife'))
+ ])
)
.then(() => dispatch(makeASandwichWithSecretSauce('Our kids')))
.then(() =>
dispatch(
getState().myMoney > 42
? withdrawMoney(42)
- : apologize('Me', 'The Sandwich Shop'),
- ),
- );
- };
+ : apologize('Me', 'The Sandwich Shop')
+ )
+ )
+ }
}
// This is very useful for server side rendering, because I can wait
@@ -282,73 +372,34 @@ function makeSandwichesForEverybody() {
store
.dispatch(makeSandwichesForEverybody())
.then(() =>
- response.send(ReactDOMServer.renderToString()),
- );
+ 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';
+import { connect } from 'react-redux'
+import { Component } from 'react'
class SandwichShop extends Component {
componentDidMount() {
- this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson));
+ this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))
}
componentDidUpdate(prevProps) {
if (prevProps.forPerson !== this.props.forPerson) {
- this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson));
+ this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))
}
}
render() {
- return
{this.props.sandwiches.join('mustard')}
;
+ 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.
-Using ES2015 shorthand property names can make this more concise.
-
-```js
-const api = "http://www.example.com/sandwiches/";
-const whatever = 42;
-
-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
- };
-}
+export default connect(state => ({
+ sandwiches: state.sandwiches
+}))(SandwichShop)
```
## License