source: node_modules/redux-thunk/README.md@ a762898

Last change on this file since a762898 was a762898, checked in by istevanoska <ilinastevanoska@…>, 5 months ago

Added visualizations

  • Property mode set to 100644
File size: 12.1 KB
RevLine 
[a762898]1# Redux Thunk
2
3Thunk [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.
4
5For complete usage instructions and useful patterns, see the [Redux docs **Writing Logic with Thunks** page](https://redux.js.org/usage/writing-logic-thunks).
6
7![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-thunk/test.yml?branch=master)
8[![npm version](https://img.shields.io/npm/v/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)
9[![npm downloads](https://img.shields.io/npm/dm/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)
10
11## Installation and Setup
12
13### Redux Toolkit
14
15If 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:
16
17```js
18import { configureStore } from '@reduxjs/toolkit'
19
20import todosReducer from './features/todos/todosSlice'
21import filtersReducer from './features/filters/filtersSlice'
22
23const store = configureStore({
24 reducer: {
25 todos: todosReducer,
26 filters: filtersReducer
27 }
28})
29
30// The thunk middleware was automatically added
31```
32
33### Manual Setup
34
35If you're using the basic Redux `createStore` API and need to set this up manually, first add the `redux-thunk` package:
36
37```sh
38npm install redux-thunk
39
40yarn add redux-thunk
41```
42
43The thunk middleware is the default export.
44
45<details>
46<summary><b>More Details: Importing the thunk middleware</b></summary>
47
48If you're using ES modules:
49
50```js
51import thunk from 'redux-thunk' // no changes here 😀
52```
53
54If you use Redux Thunk 2.x in a CommonJS environment,
55[don’t forget to add `.default` to your import](https://github.com/reduxjs/redux-thunk/releases/tag/v2.0.0):
56
57```diff
58- const thunk = require('redux-thunk')
59+ const thunk = require('redux-thunk').default
60```
61
62Additionally, since 2.x, we also support a
63[UMD build](https://unpkg.com/redux-thunk/dist/redux-thunk.min.js) for use as a global script tag:
64
65```js
66const ReduxThunk = window.ReduxThunk
67```
68
69</details>
70
71Then, to enable Redux Thunk, use
72[`applyMiddleware()`](https://redux.js.org/api/applymiddleware):
73
74```js
75import { createStore, applyMiddleware } from 'redux'
76import thunk from 'redux-thunk'
77import rootReducer from './reducers/index'
78
79const store = createStore(rootReducer, applyMiddleware(thunk))
80```
81
82### Injecting a Custom Argument
83
84Since 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.
85
86For Redux Toolkit, the `getDefaultMiddleware` callback inside of `configureStore` lets you pass in a custom `extraArgument`:
87
88```js
89import { configureStore } from '@reduxjs/toolkit'
90import rootReducer from './reducer'
91import { myCustomApiService } from './api'
92
93const store = configureStore({
94 reducer: rootReducer,
95 middleware: getDefaultMiddleware =>
96 getDefaultMiddleware({
97 thunk: {
98 extraArgument: myCustomApiService
99 }
100 })
101})
102
103// later
104function fetchUser(id) {
105 // The `extraArgument` is the third arg for thunk functions
106 return (dispatch, getState, api) => {
107 // you can use api here
108 }
109}
110```
111
112If you need to pass in multiple values, combine them into a single object:
113
114```js
115const store = configureStore({
116 reducer: rootReducer,
117 middleware: getDefaultMiddleware =>
118 getDefaultMiddleware({
119 thunk: {
120 extraArgument: {
121 api: myCustomApiService,
122 otherValue: 42
123 }
124 }
125 })
126})
127
128// later
129function fetchUser(id) {
130 return (dispatch, getState, { api, otherValue }) => {
131 // you can use api and something else here
132 }
133}
134```
135
136If you're setting up the store by hand, the named export `withExtraArgument()` function should be used to generate the correct thunk middleware:
137
138```js
139const store = createStore(reducer, applyMiddleware(withExtraArgument(api)))
140```
141
142## Why Do I Need This?
143
144With a plain basic Redux store, you can only do simple synchronous updates by
145dispatching an action. Middleware extends the store's abilities, and lets you
146write async logic that interacts with the store.
147
148Thunks are the recommended middleware for basic Redux side effects logic,
149including complex synchronous logic that needs access to the store, and simple
150async logic like AJAX requests.
151
152For more details on why thunks are useful, see:
153
154- **Redux docs: Writing Logic with Thunks**
155 https://redux.js.org/usage/writing-logic-thunks
156 The official usage guide page on thunks. Covers why they exist, how the thunk middleware works, and useful patterns for using thunks.
157
158- **Stack Overflow: Dispatching Redux Actions with a Timeout**
159 http://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559
160 Dan Abramov explains the basics of managing async behavior in Redux, walking
161 through a progressive series of approaches (inline async calls, async action
162 creators, thunk middleware).
163
164- **Stack Overflow: Why do we need middleware for async flow in Redux?**
165 http://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux/34599594#34599594
166 Dan Abramov gives reasons for using thunks and async middleware, and some
167 useful patterns for using thunks.
168
169- **What the heck is a "thunk"?**
170 https://daveceddia.com/what-is-a-thunk/
171 A quick explanation for what the word "thunk" means in general, and for Redux
172 specifically.
173
174- **Thunks in Redux: The Basics**
175 https://medium.com/fullstack-academy/thunks-in-redux-the-basics-85e538a3fe60
176 A detailed look at what thunks are, what they solve, and how to use them.
177
178You may also want to read the
179**[Redux FAQ entry on choosing which async middleware to use](https://redux.js.org/faq/actions#what-async-middleware-should-i-use-how-do-you-decide-between-thunks-sagas-observables-or-something-else)**.
180
181While the thunk middleware is not directly included with the Redux core library,
182it is used by default in our
183**[`@reduxjs/toolkit` package](https://github.com/reduxjs/redux-toolkit)**.
184
185## Motivation
186
187Redux Thunk [middleware](https://redux.js.org/tutorials/fundamentals/part-4-store#middleware)
188allows you to write action creators that return a function instead of an action.
189The thunk can be used to delay the dispatch of an action, or to dispatch only if
190a certain condition is met. The inner function receives the store methods
191`dispatch` and `getState` as parameters.
192
193An action creator that returns a function to perform asynchronous dispatch:
194
195```js
196const INCREMENT_COUNTER = 'INCREMENT_COUNTER'
197
198function increment() {
199 return {
200 type: INCREMENT_COUNTER
201 }
202}
203
204function incrementAsync() {
205 return dispatch => {
206 setTimeout(() => {
207 // Yay! Can invoke sync or async actions with `dispatch`
208 dispatch(increment())
209 }, 1000)
210 }
211}
212```
213
214An action creator that returns a function to perform conditional dispatch:
215
216```js
217function incrementIfOdd() {
218 return (dispatch, getState) => {
219 const { counter } = getState()
220
221 if (counter % 2 === 0) {
222 return
223 }
224
225 dispatch(increment())
226 }
227}
228```
229
230## What’s a thunk?!
231
232A [thunk](https://en.wikipedia.org/wiki/Thunk) is a function that wraps an
233expression to delay its evaluation.
234
235```js
236// calculation of 1 + 2 is immediate
237// x === 3
238let x = 1 + 2
239
240// calculation of 1 + 2 is delayed
241// foo can be called later to perform the calculation
242// foo is a thunk!
243let foo = () => 1 + 2
244```
245
246The term [originated](https://en.wikipedia.org/wiki/Thunk#cite_note-1) as a
247humorous past-tense version of "think".
248
249## Composition
250
251Any return value from the inner function will be available as the return value
252of `dispatch` itself. This is convenient for orchestrating an asynchronous
253control flow with thunk action creators dispatching each other and returning
254Promises to wait for each other’s completion:
255
256```js
257import { createStore, applyMiddleware } from 'redux'
258import thunk from 'redux-thunk'
259import rootReducer from './reducers'
260
261// Note: this API requires redux@>=3.1.0
262const store = createStore(rootReducer, applyMiddleware(thunk))
263
264function fetchSecretSauce() {
265 return fetch('https://www.google.com/search?q=secret+sauce')
266}
267
268// These are the normal action creators you have seen so far.
269// The actions they return can be dispatched without any middleware.
270// However, they only express “facts” and not the “async flow”.
271
272function makeASandwich(forPerson, secretSauce) {
273 return {
274 type: 'MAKE_SANDWICH',
275 forPerson,
276 secretSauce
277 }
278}
279
280function apologize(fromPerson, toPerson, error) {
281 return {
282 type: 'APOLOGIZE',
283 fromPerson,
284 toPerson,
285 error
286 }
287}
288
289function withdrawMoney(amount) {
290 return {
291 type: 'WITHDRAW',
292 amount
293 }
294}
295
296// Even without middleware, you can dispatch an action:
297store.dispatch(withdrawMoney(100))
298
299// But what do you do when you need to start an asynchronous action,
300// such as an API call, or a router transition?
301
302// Meet thunks.
303// A thunk in this context is a function that can be dispatched to perform async
304// activity and can dispatch actions and read state.
305// This is an action creator that returns a thunk:
306function makeASandwichWithSecretSauce(forPerson) {
307 // We can invert control here by returning a function - the "thunk".
308 // When this function is passed to `dispatch`, the thunk middleware will intercept it,
309 // and call it with `dispatch` and `getState` as arguments.
310 // This gives the thunk function the ability to run some logic, and still interact with the store.
311 return function (dispatch) {
312 return fetchSecretSauce().then(
313 sauce => dispatch(makeASandwich(forPerson, sauce)),
314 error => dispatch(apologize('The Sandwich Shop', forPerson, error))
315 )
316 }
317}
318
319// Thunk middleware lets me dispatch thunk async actions
320// as if they were actions!
321
322store.dispatch(makeASandwichWithSecretSauce('Me'))
323
324// It even takes care to return the thunk’s return value
325// from the dispatch, so I can chain Promises as long as I return them.
326
327store.dispatch(makeASandwichWithSecretSauce('My partner')).then(() => {
328 console.log('Done!')
329})
330
331// In fact I can write action creators that dispatch
332// actions and async actions from other action creators,
333// and I can build my control flow with Promises.
334
335function makeSandwichesForEverybody() {
336 return function (dispatch, getState) {
337 if (!getState().sandwiches.isShopOpen) {
338 // You don’t have to return Promises, but it’s a handy convention
339 // so the caller can always call .then() on async dispatch result.
340
341 return Promise.resolve()
342 }
343
344 // We can dispatch both plain object actions and other thunks,
345 // which lets us compose the asynchronous actions in a single flow.
346
347 return dispatch(makeASandwichWithSecretSauce('My Grandma'))
348 .then(() =>
349 Promise.all([
350 dispatch(makeASandwichWithSecretSauce('Me')),
351 dispatch(makeASandwichWithSecretSauce('My wife'))
352 ])
353 )
354 .then(() => dispatch(makeASandwichWithSecretSauce('Our kids')))
355 .then(() =>
356 dispatch(
357 getState().myMoney > 42
358 ? withdrawMoney(42)
359 : apologize('Me', 'The Sandwich Shop')
360 )
361 )
362 }
363}
364
365// This is very useful for server side rendering, because I can wait
366// until data is available, then synchronously render the app.
367
368store
369 .dispatch(makeSandwichesForEverybody())
370 .then(() =>
371 response.send(ReactDOMServer.renderToString(<MyApp store={store} />))
372 )
373
374// I can also dispatch a thunk async action from a component
375// any time its props change to load the missing data.
376
377import { connect } from 'react-redux'
378import { Component } from 'react'
379
380class SandwichShop extends Component {
381 componentDidMount() {
382 this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))
383 }
384
385 componentDidUpdate(prevProps) {
386 if (prevProps.forPerson !== this.props.forPerson) {
387 this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))
388 }
389 }
390
391 render() {
392 return <p>{this.props.sandwiches.join('mustard')}</p>
393 }
394}
395
396export default connect(state => ({
397 sandwiches: state.sandwiches
398}))(SandwichShop)
399```
400
401## License
402
403MIT
Note: See TracBrowser for help on using the repository browser.