Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Thunk middleware for Redux

License

NotificationsYou must be signed in to change notification settings

reduxjs/redux-thunk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thunkmiddleware for Redux. It allows writing functions with logic inside that can interact with a Redux store'sdispatch andgetState methods.

For complete usage instructions and useful patterns, see theRedux docsWriting Logic with Thunks page.

GitHub Workflow Statusnpm versionnpm downloads

Installation and Setup

Redux Toolkit

If you're usingour official Redux Toolkit package as recommended, there's nothing to install - RTK'sconfigureStore API already adds the thunk middleware by default:

import{configureStore}from'@reduxjs/toolkit'importtodosReducerfrom'./features/todos/todosSlice'importfiltersReducerfrom'./features/filters/filtersSlice'conststore=configureStore({reducer:{todos:todosReducer,filters:filtersReducer,},})// The thunk middleware was automatically added

Manual Setup

If you're using the basic ReduxcreateStore API and need to set this up manually, first add theredux-thunk package:

npm install redux-thunkyarn add redux-thunk

The thunk middleware is a named export.

More Details: Importing the thunk middleware

If you're using ES modules:

import{thunk}from'redux-thunk'

If you use Redux Thunk in a CommonJS environment:

const{ thunk}=require('redux-thunk')

Then, to enable Redux Thunk, useapplyMiddleware():

import{createStore,applyMiddleware}from'redux'import{thunk}from'redux-thunk'importrootReducerfrom'./reducers/index'conststore=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, thegetDefaultMiddleware callback inside ofconfigureStore lets you pass in a customextraArgument:

import{configureStore}from'@reduxjs/toolkit'importrootReducerfrom'./reducer'import{myCustomApiService}from'./api'conststore=configureStore({reducer:rootReducer,middleware:getDefaultMiddleware=>getDefaultMiddleware({thunk:{extraArgument:myCustomApiService,},}),})// laterfunctionfetchUser(id){// The `extraArgument` is the third arg for thunk functionsreturn(dispatch,getState,api)=>{// you can use api here}}

If you need to pass in multiple values, combine them into a single object:

conststore=configureStore({reducer:rootReducer,middleware:getDefaultMiddleware=>getDefaultMiddleware({thunk:{extraArgument:{api:myCustomApiService,otherValue:42,},},}),})// laterfunctionfetchUser(id){return(dispatch,getState,{ api, otherValue})=>{// you can use api and something else here}}

If you're setting up the store by hand, the named exportwithExtraArgument() function should be used to generate the correct thunk middleware:

conststore=createStore(reducer,applyMiddleware(withExtraArgument(api)))

Why Do I Need This?

With a plain basic Redux store, you can only do simple synchronous updates bydispatching an action. Middleware extends the store's abilities, and lets youwrite async logic that interacts with the store.

Thunks are the recommended middleware for basic Redux side effects logic,including complex synchronous logic that needs access to the store, and simpleasync logic like AJAX requests.

For more details on why thunks are useful, see:

You may also want to read theRedux FAQ entry on choosing which async middleware to use.

While the thunk middleware is not directly included with the Redux core library,it is used by default in our@reduxjs/toolkit package.

Motivation

Redux Thunkmiddlewareallows 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 ifa certain condition is met. The inner function receives the store methodsdispatch andgetState as parameters.

An action creator that returns a function to perform asynchronous dispatch:

constINCREMENT_COUNTER='INCREMENT_COUNTER'functionincrement(){return{type:INCREMENT_COUNTER,}}functionincrementAsync(){returndispatch=>{setTimeout(()=>{// Yay! Can invoke sync or async actions with `dispatch`dispatch(increment())},1000)}}

An action creator that returns a function to perform conditional dispatch:

functionincrementIfOdd(){return(dispatch,getState)=>{const{ counter}=getState()if(counter%2===0){return}dispatch(increment())}}

What’s a thunk?!

Athunk is a function that wraps anexpression to delay its evaluation.

// calculation of 1 + 2 is immediate// x === 3letx=1+2// calculation of 1 + 2 is delayed// foo can be called later to perform the calculation// foo is a thunk!letfoo=()=>1+2

The termoriginated as ahumorous past-tense version of "think".

Composition

Any return value from the inner function will be available as the return valueofdispatch itself. This is convenient for orchestrating an asynchronouscontrol flow with thunk action creators dispatching each other and returningPromises to wait for each other’s completion:

import{createStore,applyMiddleware}from'redux'import{thunk}from'redux-thunk'importrootReducerfrom'./reducers'// Note: this API requires redux@>=3.1.0conststore=createStore(rootReducer,applyMiddleware(thunk))functionfetchSecretSauce(){returnfetch('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”.functionmakeASandwich(forPerson,secretSauce){return{type:'MAKE_SANDWICH',    forPerson,    secretSauce,}}functionapologize(fromPerson,toPerson,error){return{type:'APOLOGIZE',    fromPerson,    toPerson,    error,}}functionwithdrawMoney(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 in this context is a function that can be dispatched to perform async// activity and can dispatch actions and read state.// This is an action creator that returns a thunk:functionmakeASandwichWithSecretSauce(forPerson){// We can invert control here by returning a function - the "thunk".// 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.returnfunction(dispatch){returnfetchSecretSauce().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 partner')).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.functionmakeSandwichesForEverybody(){returnfunction(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.returnPromise.resolve()}// We can dispatch both plain object actions and other thunks,// which lets us compose the asynchronous actions in a single flow.returndispatch(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(<MyAppstore={store}/>)),)// 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'classSandwichShopextendsComponent{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<p>{this.props.sandwiches.join('mustard')}</p>}}exportdefaultconnect(state=>({sandwiches:state.sandwiches,}))(SandwichShop)

License

MIT


[8]ページ先頭

©2009-2025 Movatter.jp