Using Redux Thunk for Async Actions in Redux
Learn how Redux Thunk lets action creators return functions for async work, with a concrete store setup, example thunk, limits, and common pitfalls.
19 Sept 2025, 03:48 UTC

Use redux-thunk to run async logic from action creators
Redux reducers must stay pure and synchronous. redux-thunk middleware lets an action creator return a function instead of a plain object. The middleware calls that function with dispatch and getState, so you can start async work and then dispatch normal actions to update state.
Store setup with redux-thunk
First install the packages:
npm install redux redux-thunkThen create the store, applying the thunk middleware:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;Example thunk action creator
This example shows a thunk that fetches a user simulated with setTimeout and dispatches request, success, and failure actions to reflect loading states.
// action types
const FETCH_REQUEST = 'FETCH_REQUEST';
const FETCH_SUCCESS = 'FETCH_SUCCESS';
const FETCH_FAILURE = 'FETCH_FAILURE';
// action creators
const fetchRequest = () => ({ type: FETCH_REQUEST });
const fetchSuccess = payload => ({ type: FETCH_SUCCESS, payload });
const fetchFailure = error => ({ type: FETCH_FAILURE, error });
// thunk
export const fetchUser = userId => {
return (dispatch, getState) => {
dispatch(fetchRequest());
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId > 0) {
resolve({ id: userId, name: 'Example User' });
} else {
reject(new Error('Invalid user ID'));
}
}, 500);
})
.then(data => {
dispatch(fetchSuccess(data));
resolve(data);
})
.catch(err => {
dispatch(fetchFailure(err.message));
reject(err);
});
};
};When you dispatch store.dispatch(fetchUser(42)), Redux Thunk invokes the returned function. The function first dispatches FETCH_REQUEST, performs the async timeout, and then dispatches either FETCH_SUCCESS or FETCH_FAILURE. Subscribers see these actions in order and can update UI accordingly.
Limits and common mistakes
- Synchronous execution: Middleware runs synchronously. If a thunk performs a long-running blocking operation it will block the UI thread. Off-load CPU-intensive work to a Web Worker or keep async work truly asynchronous.
- Error handling: Errors thrown inside a thunk propagate unless caught. Forgetting a try/catch or not handling promise rejections can cause silent failures. Always wrap async code or use .catch as shown.
- Returning the thunk: The action creator must return the function. Returning nothing or a plain object will cause the middleware to ignore it.
- Dispatching plain actions from within the thunk: Only call dispatch with plain action objects. Dispatching another function works only if middleware supports it.
- Mutable state in reducers: Thunks do not change reducer rules. Reducers must stay pure and immutable.
How to verify the behavior
- Create a minimal React or plain-JS page that imports the store.
- Subscribe to store changes: store.subscribe(() => console.log(store.getState())).
- Dispatch fetchUser(1) and observe console logs showing state after FETCH_REQUEST then after FETCH_SUCCESS.
- Dispatch fetchUser(-1) and verify that a FETCH_FAILURE action appears.
- To test error handling, throw an error inside the promise and ensure the failure action is dispatched.
These steps confirm that the middleware intercepts the function, async work runs, and plain actions reach reducers.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.