Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. Redux Thunk
  2. =============
  3. Thunk [middleware](https://redux.js.org/advanced/middleware) for Redux.
  4. [![build status](https://img.shields.io/travis/reduxjs/redux-thunk/master.svg?style=flat-square)](https://travis-ci.org/reduxjs/redux-thunk)
  5. [![npm version](https://img.shields.io/npm/v/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)
  6. [![npm downloads](https://img.shields.io/npm/dm/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)
  7. ```js
  8. npm install --save redux-thunk
  9. ```
  10. ## Note on 2.x Update
  11. Most tutorials today assume Redux Thunk 1.x so you might run into an issue when running their code with 2.x.
  12. **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):**
  13. ```diff
  14. - var ReduxThunk = require('redux-thunk')
  15. + var ReduxThunk = require('redux-thunk').default
  16. ```
  17. If you used ES modules, you’re already all good:
  18. ```js
  19. import ReduxThunk from 'redux-thunk' // no changes here 😀
  20. ```
  21. Additionally, since 2.x, we also support a [UMD build](https://unpkg.com/redux-thunk/dist/redux-thunk.min.js):
  22. ```js
  23. var ReduxThunk = window.ReduxThunk.default
  24. ```
  25. As you can see, it also requires `.default` at the end.
  26. ## Why Do I Need This?
  27. If you’re not sure whether you need it, you probably don’t.
  28. **[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)**
  29. ## Motivation
  30. 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.
  31. An action creator that returns a function to perform asynchronous dispatch:
  32. ```js
  33. const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
  34. function increment() {
  35. return {
  36. type: INCREMENT_COUNTER
  37. };
  38. }
  39. function incrementAsync() {
  40. return dispatch => {
  41. setTimeout(() => {
  42. // Yay! Can invoke sync or async actions with `dispatch`
  43. dispatch(increment());
  44. }, 1000);
  45. };
  46. }
  47. ```
  48. An action creator that returns a function to perform conditional dispatch:
  49. ```js
  50. function incrementIfOdd() {
  51. return (dispatch, getState) => {
  52. const { counter } = getState();
  53. if (counter % 2 === 0) {
  54. return;
  55. }
  56. dispatch(increment());
  57. };
  58. }
  59. ```
  60. ## What’s a thunk?!
  61. A [thunk](https://en.wikipedia.org/wiki/Thunk) is a function that wraps an expression to delay its evaluation.
  62. ```js
  63. // calculation of 1 + 2 is immediate
  64. // x === 3
  65. let x = 1 + 2;
  66. // calculation of 1 + 2 is delayed
  67. // foo can be called later to perform the calculation
  68. // foo is a thunk!
  69. let foo = () => 1 + 2;
  70. ```
  71. The term [originated](https://en.wikipedia.org/wiki/Thunk#cite_note-1) as a humorous past-tense version of "think".
  72. ## Installation
  73. ```
  74. npm install --save redux-thunk
  75. ```
  76. Then, to enable Redux Thunk, use [`applyMiddleware()`](https://redux.js.org/api-reference/applymiddleware):
  77. ```js
  78. import { createStore, applyMiddleware } from 'redux';
  79. import thunk from 'redux-thunk';
  80. import rootReducer from './reducers/index';
  81. // Note: this API requires redux@>=3.1.0
  82. const store = createStore(
  83. rootReducer,
  84. applyMiddleware(thunk)
  85. );
  86. ```
  87. ## Composition
  88. 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:
  89. ```js
  90. import { createStore, applyMiddleware } from 'redux';
  91. import thunk from 'redux-thunk';
  92. import rootReducer from './reducers';
  93. // Note: this API requires redux@>=3.1.0
  94. const store = createStore(
  95. rootReducer,
  96. applyMiddleware(thunk)
  97. );
  98. function fetchSecretSauce() {
  99. return fetch('https://www.google.com/search?q=secret+sauce');
  100. }
  101. // These are the normal action creators you have seen so far.
  102. // The actions they return can be dispatched without any middleware.
  103. // However, they only express “facts” and not the “async flow”.
  104. function makeASandwich(forPerson, secretSauce) {
  105. return {
  106. type: 'MAKE_SANDWICH',
  107. forPerson,
  108. secretSauce
  109. };
  110. }
  111. function apologize(fromPerson, toPerson, error) {
  112. return {
  113. type: 'APOLOGIZE',
  114. fromPerson,
  115. toPerson,
  116. error
  117. };
  118. }
  119. function withdrawMoney(amount) {
  120. return {
  121. type: 'WITHDRAW',
  122. amount
  123. };
  124. }
  125. // Even without middleware, you can dispatch an action:
  126. store.dispatch(withdrawMoney(100));
  127. // But what do you do when you need to start an asynchronous action,
  128. // such as an API call, or a router transition?
  129. // Meet thunks.
  130. // A thunk is a function that returns a function.
  131. // This is a thunk.
  132. function makeASandwichWithSecretSauce(forPerson) {
  133. // Invert control!
  134. // Return a function that accepts `dispatch` so we can dispatch later.
  135. // Thunk middleware knows how to turn thunk async actions into actions.
  136. return function (dispatch) {
  137. return fetchSecretSauce().then(
  138. sauce => dispatch(makeASandwich(forPerson, sauce)),
  139. error => dispatch(apologize('The Sandwich Shop', forPerson, error))
  140. );
  141. };
  142. }
  143. // Thunk middleware lets me dispatch thunk async actions
  144. // as if they were actions!
  145. store.dispatch(
  146. makeASandwichWithSecretSauce('Me')
  147. );
  148. // It even takes care to return the thunk’s return value
  149. // from the dispatch, so I can chain Promises as long as I return them.
  150. store.dispatch(
  151. makeASandwichWithSecretSauce('My wife')
  152. ).then(() => {
  153. console.log('Done!');
  154. });
  155. // In fact I can write action creators that dispatch
  156. // actions and async actions from other action creators,
  157. // and I can build my control flow with Promises.
  158. function makeSandwichesForEverybody() {
  159. return function (dispatch, getState) {
  160. if (!getState().sandwiches.isShopOpen) {
  161. // You don’t have to return Promises, but it’s a handy convention
  162. // so the caller can always call .then() on async dispatch result.
  163. return Promise.resolve();
  164. }
  165. // We can dispatch both plain object actions and other thunks,
  166. // which lets us compose the asynchronous actions in a single flow.
  167. return dispatch(
  168. makeASandwichWithSecretSauce('My Grandma')
  169. ).then(() =>
  170. Promise.all([
  171. dispatch(makeASandwichWithSecretSauce('Me')),
  172. dispatch(makeASandwichWithSecretSauce('My wife'))
  173. ])
  174. ).then(() =>
  175. dispatch(makeASandwichWithSecretSauce('Our kids'))
  176. ).then(() =>
  177. dispatch(getState().myMoney > 42 ?
  178. withdrawMoney(42) :
  179. apologize('Me', 'The Sandwich Shop')
  180. )
  181. );
  182. };
  183. }
  184. // This is very useful for server side rendering, because I can wait
  185. // until data is available, then synchronously render the app.
  186. store.dispatch(
  187. makeSandwichesForEverybody()
  188. ).then(() =>
  189. response.send(ReactDOMServer.renderToString(<MyApp store={store} />))
  190. );
  191. // I can also dispatch a thunk async action from a component
  192. // any time its props change to load the missing data.
  193. import { connect } from 'react-redux';
  194. import { Component } from 'react';
  195. class SandwichShop extends Component {
  196. componentDidMount() {
  197. this.props.dispatch(
  198. makeASandwichWithSecretSauce(this.props.forPerson)
  199. );
  200. }
  201. componentDidUpdate(prevProps) {
  202. if (prevProps.forPerson !== this.props.forPerson) {
  203. this.props.dispatch(
  204. makeASandwichWithSecretSauce(this.props.forPerson)
  205. );
  206. }
  207. }
  208. render() {
  209. return <p>{this.props.sandwiches.join('mustard')}</p>
  210. }
  211. }
  212. export default connect(
  213. state => ({
  214. sandwiches: state.sandwiches
  215. })
  216. )(SandwichShop);
  217. ```
  218. ## Injecting a Custom Argument
  219. Since 2.1.0, Redux Thunk supports injecting a custom argument using the `withExtraArgument` function:
  220. ```js
  221. const store = createStore(
  222. reducer,
  223. applyMiddleware(thunk.withExtraArgument(api))
  224. )
  225. // later
  226. function fetchUser(id) {
  227. return (dispatch, getState, api) => {
  228. // you can use api here
  229. }
  230. }
  231. ```
  232. To pass multiple things, just wrap them in a single object and use destructuring:
  233. ```js
  234. const store = createStore(
  235. reducer,
  236. applyMiddleware(thunk.withExtraArgument({ api, whatever }))
  237. )
  238. // later
  239. function fetchUser(id) {
  240. return (dispatch, getState, { api, whatever }) => {
  241. // you can use api and something else here
  242. }
  243. }
  244. ```
  245. ## License
  246. MIT