{"id":13451819,"url":"https://github.com/reduxjs/redux-thunk","last_synced_at":"2026-05-10T05:01:36.419Z","repository":{"id":34941566,"uuid":"39013909","full_name":"reduxjs/redux-thunk","owner":"reduxjs","description":"Thunk middleware for Redux","archived":false,"fork":false,"pushed_at":"2025-03-16T00:36:10.000Z","size":3427,"stargazers_count":17714,"open_issues_count":1,"forks_count":1021,"subscribers_count":164,"default_branch":"master","last_synced_at":"2026-04-27T20:04:58.948Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reduxjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-13T13:33:08.000Z","updated_at":"2026-04-27T20:03:45.000Z","dependencies_parsed_at":"2023-02-12T07:30:29.018Z","dependency_job_id":"e0d25870-e41c-4a56-a197-5167b50195cc","html_url":"https://github.com/reduxjs/redux-thunk","commit_stats":{"total_commits":178,"total_committers":55,"mean_commits":"3.2363636363636363","dds":0.6910112359550562,"last_synced_commit":"98bffb1c547fb0dac2d8ebe002778bc644d3b2d2"},"previous_names":["gaearon/redux-thunk"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/reduxjs/redux-thunk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reduxjs%2Fredux-thunk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reduxjs%2Fredux-thunk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reduxjs%2Fredux-thunk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reduxjs%2Fredux-thunk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reduxjs","download_url":"https://codeload.github.com/reduxjs/redux-thunk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reduxjs%2Fredux-thunk/sbom","scorecard":{"id":416957,"data":{"date":"2025-08-11","repo":{"name":"github.com/reduxjs/redux-thunk","commit":"184205d49f707c6f203269e0d39ad85824801816"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Code-Review","score":6,"reason":"Found 13/20 approved changesets -- score normalized to 6","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:152: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:155: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:164: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:178: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:216: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:219: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:224: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:66: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:97: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:100: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:106: update your workflow using https://app.stepsecurity.io/secureworkflow/reduxjs/redux-thunk/test.yml/master?enable=pin","Info:   0 out of  16 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 24 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":2,"reason":"8 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-3mv9-4h5g-vhg3","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T00:09:56.133Z","repository_id":34941566,"created_at":"2025-08-19T00:09:56.133Z","updated_at":"2025-08-19T00:09:56.133Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32379629,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T11:25:28.583Z","status":"ssl_error","status_checked_at":"2026-04-28T11:25:05.435Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T07:01:03.683Z","updated_at":"2026-05-10T05:01:36.372Z","avatar_url":"https://github.com/reduxjs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","JavaScript","**🏎 Core technologies**","📦 Legacy \u0026 Inactive Projects","Uncategorized","框架","⚛️ React","Redux [🔝](#readme)","目录"],"sub_categories":["Uncategorized","React.js","State management","\u003ca id=\"state\"\u003e状态管理\u003c/a\u003e"],"readme":"# Redux Thunk\n\nThunk [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.\n\nFor complete usage instructions and useful patterns, see the [Redux docs **Writing Logic with Thunks** page](https://redux.js.org/usage/writing-logic-thunks).\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-thunk/test.yml?branch=master)\n[![npm version](https://img.shields.io/npm/v/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)\n[![npm downloads](https://img.shields.io/npm/dm/redux-thunk.svg?style=flat-square)](https://www.npmjs.com/package/redux-thunk)\n\n## Installation and Setup\n\n### Redux Toolkit\n\nIf 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:\n\n```js\nimport { configureStore } from '@reduxjs/toolkit'\n\nimport todosReducer from './features/todos/todosSlice'\nimport filtersReducer from './features/filters/filtersSlice'\n\nconst store = configureStore({\n  reducer: {\n    todos: todosReducer,\n    filters: filtersReducer,\n  },\n})\n\n// The thunk middleware was automatically added\n```\n\n### Manual Setup\n\nIf you're using the basic Redux `createStore` API and need to set this up manually, first add the `redux-thunk` package:\n\n```sh\nnpm install redux-thunk\n\nyarn add redux-thunk\n```\n\nThe thunk middleware is a named export.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMore Details: Importing the thunk middleware\u003c/b\u003e\u003c/summary\u003e\n\nIf you're using ES modules:\n\n```js\nimport { thunk } from 'redux-thunk'\n```\n\nIf you use Redux Thunk in a CommonJS environment:\n\n```js\nconst { thunk } = require('redux-thunk')\n```\n\n\u003c/details\u003e\n\nThen, to enable Redux Thunk, use\n[`applyMiddleware()`](https://redux.js.org/api/applymiddleware):\n\n```js\nimport { createStore, applyMiddleware } from 'redux'\nimport { thunk } from 'redux-thunk'\nimport rootReducer from './reducers/index'\n\nconst store = createStore(rootReducer, applyMiddleware(thunk))\n```\n\n### Injecting a Custom Argument\n\nSince 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.\n\nFor Redux Toolkit, the `getDefaultMiddleware` callback inside of `configureStore` lets you pass in a custom `extraArgument`:\n\n```js\nimport { configureStore } from '@reduxjs/toolkit'\nimport rootReducer from './reducer'\nimport { myCustomApiService } from './api'\n\nconst store = configureStore({\n  reducer: rootReducer,\n  middleware: getDefaultMiddleware =\u003e\n    getDefaultMiddleware({\n      thunk: {\n        extraArgument: myCustomApiService,\n      },\n    }),\n})\n\n// later\nfunction fetchUser(id) {\n  // The `extraArgument` is the third arg for thunk functions\n  return (dispatch, getState, api) =\u003e {\n    // you can use api here\n  }\n}\n```\n\nIf you need to pass in multiple values, combine them into a single object:\n\n```js\nconst store = configureStore({\n  reducer: rootReducer,\n  middleware: getDefaultMiddleware =\u003e\n    getDefaultMiddleware({\n      thunk: {\n        extraArgument: {\n          api: myCustomApiService,\n          otherValue: 42,\n        },\n      },\n    }),\n})\n\n// later\nfunction fetchUser(id) {\n  return (dispatch, getState, { api, otherValue }) =\u003e {\n    // you can use api and something else here\n  }\n}\n```\n\nIf you're setting up the store by hand, the named export `withExtraArgument()` function should be used to generate the correct thunk middleware:\n\n```js\nconst store = createStore(reducer, applyMiddleware(withExtraArgument(api)))\n```\n\n## Why Do I Need This?\n\nWith a plain basic Redux store, you can only do simple synchronous updates by\ndispatching an action. Middleware extends the store's abilities, and lets you\nwrite async logic that interacts with the store.\n\nThunks are the recommended middleware for basic Redux side effects logic,\nincluding complex synchronous logic that needs access to the store, and simple\nasync logic like AJAX requests.\n\nFor more details on why thunks are useful, see:\n\n- **Redux docs: Writing Logic with Thunks**  \n  https://redux.js.org/usage/writing-logic-thunks  \n  The official usage guide page on thunks. Covers why they exist, how the thunk middleware works, and useful patterns for using thunks.\n\n- **Stack Overflow: Dispatching Redux Actions with a Timeout**  \n  http://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559  \n  Dan Abramov explains the basics of managing async behavior in Redux, walking\n  through a progressive series of approaches (inline async calls, async action\n  creators, thunk middleware).\n\n- **Stack Overflow: Why do we need middleware for async flow in Redux?**  \n  http://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux/34599594#34599594  \n  Dan Abramov gives reasons for using thunks and async middleware, and some\n  useful patterns for using thunks.\n\n- **What the heck is a \"thunk\"?**  \n  https://daveceddia.com/what-is-a-thunk/  \n  A quick explanation for what the word \"thunk\" means in general, and for Redux\n  specifically.\n\n- **Thunks in Redux: The Basics**  \n  https://medium.com/fullstack-academy/thunks-in-redux-the-basics-85e538a3fe60  \n  A detailed look at what thunks are, what they solve, and how to use them.\n\nYou may also want to read the\n**[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)**.\n\nWhile the thunk middleware is not directly included with the Redux core library,\nit is used by default in our\n**[`@reduxjs/toolkit` package](https://github.com/reduxjs/redux-toolkit)**.\n\n## Motivation\n\nRedux Thunk [middleware](https://redux.js.org/tutorials/fundamentals/part-4-store#middleware)\nallows you to write action creators that return a function instead of an action.\nThe thunk can be used to delay the dispatch of an action, or to dispatch only if\na certain condition is met. The inner function receives the store methods\n`dispatch` and `getState` as parameters.\n\nAn action creator that returns a function to perform asynchronous dispatch:\n\n```js\nconst INCREMENT_COUNTER = 'INCREMENT_COUNTER'\n\nfunction increment() {\n  return {\n    type: INCREMENT_COUNTER,\n  }\n}\n\nfunction incrementAsync() {\n  return dispatch =\u003e {\n    setTimeout(() =\u003e {\n      // Yay! Can invoke sync or async actions with `dispatch`\n      dispatch(increment())\n    }, 1000)\n  }\n}\n```\n\nAn action creator that returns a function to perform conditional dispatch:\n\n```js\nfunction incrementIfOdd() {\n  return (dispatch, getState) =\u003e {\n    const { counter } = getState()\n\n    if (counter % 2 === 0) {\n      return\n    }\n\n    dispatch(increment())\n  }\n}\n```\n\n## What’s a thunk?!\n\nA [thunk](https://en.wikipedia.org/wiki/Thunk) is a function that wraps an\nexpression to delay its evaluation.\n\n```js\n// calculation of 1 + 2 is immediate\n// x === 3\nlet x = 1 + 2\n\n// calculation of 1 + 2 is delayed\n// foo can be called later to perform the calculation\n// foo is a thunk!\nlet foo = () =\u003e 1 + 2\n```\n\nThe term [originated](https://en.wikipedia.org/wiki/Thunk#cite_note-1) as a\nhumorous past-tense version of \"think\".\n\n## Composition\n\nAny return value from the inner function will be available as the return value\nof `dispatch` itself. This is convenient for orchestrating an asynchronous\ncontrol flow with thunk action creators dispatching each other and returning\nPromises to wait for each other’s completion:\n\n```js\nimport { createStore, applyMiddleware } from 'redux'\nimport { thunk } from 'redux-thunk'\nimport rootReducer from './reducers'\n\n// Note: this API requires redux@\u003e=3.1.0\nconst store = createStore(rootReducer, applyMiddleware(thunk))\n\nfunction fetchSecretSauce() {\n  return fetch('https://www.google.com/search?q=secret+sauce')\n}\n\n// These are the normal action creators you have seen so far.\n// The actions they return can be dispatched without any middleware.\n// However, they only express “facts” and not the “async flow”.\n\nfunction makeASandwich(forPerson, secretSauce) {\n  return {\n    type: 'MAKE_SANDWICH',\n    forPerson,\n    secretSauce,\n  }\n}\n\nfunction apologize(fromPerson, toPerson, error) {\n  return {\n    type: 'APOLOGIZE',\n    fromPerson,\n    toPerson,\n    error,\n  }\n}\n\nfunction withdrawMoney(amount) {\n  return {\n    type: 'WITHDRAW',\n    amount,\n  }\n}\n\n// Even without middleware, you can dispatch an action:\nstore.dispatch(withdrawMoney(100))\n\n// But what do you do when you need to start an asynchronous action,\n// such as an API call, or a router transition?\n\n// Meet thunks.\n// A thunk in this context is a function that can be dispatched to perform async\n// activity and can dispatch actions and read state.\n// This is an action creator that returns a thunk:\nfunction makeASandwichWithSecretSauce(forPerson) {\n  // We can invert control here by returning a function - the \"thunk\".\n  // When this function is passed to `dispatch`, the thunk middleware will intercept it,\n  // and call it with `dispatch` and `getState` as arguments.\n  // This gives the thunk function the ability to run some logic, and still interact with the store.\n  return function (dispatch) {\n    return fetchSecretSauce().then(\n      sauce =\u003e dispatch(makeASandwich(forPerson, sauce)),\n      error =\u003e dispatch(apologize('The Sandwich Shop', forPerson, error)),\n    )\n  }\n}\n\n// Thunk middleware lets me dispatch thunk async actions\n// as if they were actions!\n\nstore.dispatch(makeASandwichWithSecretSauce('Me'))\n\n// It even takes care to return the thunk’s return value\n// from the dispatch, so I can chain Promises as long as I return them.\n\nstore.dispatch(makeASandwichWithSecretSauce('My partner')).then(() =\u003e {\n  console.log('Done!')\n})\n\n// In fact I can write action creators that dispatch\n// actions and async actions from other action creators,\n// and I can build my control flow with Promises.\n\nfunction makeSandwichesForEverybody() {\n  return function (dispatch, getState) {\n    if (!getState().sandwiches.isShopOpen) {\n      // You don’t have to return Promises, but it’s a handy convention\n      // so the caller can always call .then() on async dispatch result.\n\n      return Promise.resolve()\n    }\n\n    // We can dispatch both plain object actions and other thunks,\n    // which lets us compose the asynchronous actions in a single flow.\n\n    return dispatch(makeASandwichWithSecretSauce('My Grandma'))\n      .then(() =\u003e\n        Promise.all([\n          dispatch(makeASandwichWithSecretSauce('Me')),\n          dispatch(makeASandwichWithSecretSauce('My wife')),\n        ]),\n      )\n      .then(() =\u003e dispatch(makeASandwichWithSecretSauce('Our kids')))\n      .then(() =\u003e\n        dispatch(\n          getState().myMoney \u003e 42\n            ? withdrawMoney(42)\n            : apologize('Me', 'The Sandwich Shop'),\n        ),\n      )\n  }\n}\n\n// This is very useful for server side rendering, because I can wait\n// until data is available, then synchronously render the app.\n\nstore\n  .dispatch(makeSandwichesForEverybody())\n  .then(() =\u003e\n    response.send(ReactDOMServer.renderToString(\u003cMyApp store={store} /\u003e)),\n  )\n\n// I can also dispatch a thunk async action from a component\n// any time its props change to load the missing data.\n\nimport { connect } from 'react-redux'\nimport { Component } from 'react'\n\nclass SandwichShop extends Component {\n  componentDidMount() {\n    this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))\n  }\n\n  componentDidUpdate(prevProps) {\n    if (prevProps.forPerson !== this.props.forPerson) {\n      this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson))\n    }\n  }\n\n  render() {\n    return \u003cp\u003e{this.props.sandwiches.join('mustard')}\u003c/p\u003e\n  }\n}\n\nexport default connect(state =\u003e ({\n  sandwiches: state.sandwiches,\n}))(SandwichShop)\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freduxjs%2Fredux-thunk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freduxjs%2Fredux-thunk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freduxjs%2Fredux-thunk/lists"}