Redux Action Creators for Promises
raw JSON →This library provides action creators designed to streamline the creation of synchronous and asynchronous Redux actions, specifically integrating with `redux-promise-middleware`. It is currently stable at version 3.1.0, and while its explicit release cadence is not specified, ongoing build status indicators suggest active maintenance. A key differentiator is its robust first-class TypeScript support, which requires TypeScript 3 or newer for version 3 of the library, and TypeScript 2 for version 1. It simplifies the process of creating async actions by automatically generating `_PENDING`, `_FULFILLED`, and `_REJECTED` actions when a promise is dispatched, thereby eliminating the need for manual action type enums. The library promotes type safety in reducers by allowing action creators to be directly referenced (e.g., `String(myAction)`) and boasts a tiny bundle size, making it a lightweight alternative focused on promise-based Redux flows without external dependencies beyond `redux-promise-middleware` itself.
Common errors
error Argument of type 'string' is not assignable to parameter of type 'AnyAction'. ↓
String(actionCreator) or actionCreator.toString() to obtain the action type string, ensuring type compatibility and leverage TypeScript's type inference. error TypeError: Cannot read properties of undefined (reading 'pending') ↓
createAsyncAction. error Promise rejected with a non-error: 'Your error message here'. Consider rejecting with an Error object to ensure `action.error` is true. ↓
Error (e.g., Promise.reject(new Error('Failed!'))). Alternatively, configure redux-promise-middleware's isError option to handle custom error types. Warnings
breaking Version 3.x of `redux-promise-middleware-actions` requires TypeScript 3.x or newer. For projects using TypeScript 2.x, `redux-promise-middleware-actions` version 1.x must be used. ↓
gotcha This library is not compatible with the `promiseTypeSuffixes` option of `redux-promise-middleware`. It uses its own fixed `_PENDING`, `_FULFILLED`, `_REJECTED` suffixes. ↓
gotcha `redux-promise-middleware-actions` requires `redux-promise-middleware` to be installed and applied to the Redux store's middleware chain to correctly handle asynchronous actions. ↓
Install
npm install redux-promise-middleware-actions yarn add redux-promise-middleware-actions pnpm add redux-promise-middleware-actions Imports
- createAction wrong
const createAction = require('redux-promise-middleware-actions').createAction;correctimport { createAction } from 'redux-promise-middleware-actions'; - createAsyncAction wrong
import createAsyncAction from 'redux-promise-middleware-actions';correctimport { createAsyncAction } from 'redux-promise-middleware-actions'; - String(actionCreator) wrong
case 'MY_ACTION_FULFILLED': // in a reducercorrectcase String(myActionCreator.fulfilled): // in a reducer
Quickstart
import { createStore, applyMiddleware, compose } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import { createAsyncAction } from 'redux-promise-middleware-actions';
// 1. Setup Redux store with promiseMiddleware
// Ensure Redux DevTools compatibility if available
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
// A simple reducer to handle our async action states
const rootReducer = (state = { data: null, loading: false, error: null }, action) => {
switch (String(action.type)) { // Use String(action.type) for type matching
case String(fetchData.pending): // Action.type for pending actions is usually the base type + _PENDING suffix
return { ...state, loading: true, error: null };
case String(fetchData.fulfilled): // For fulfilled, payload is the resolved promise value
return { ...state, loading: false, data: action.payload };
case String(fetchData.rejected): // For rejected, payload is the error object
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(promiseMiddleware))
);
// 2. Create an async action
export const fetchData = createAsyncAction('FETCH_DATA', async (id) => {
// Simulate an asynchronous operation (e.g., an API call)
console.log(`Fetching data for ID: ${id}...`);
const response = await new Promise(resolve => {
setTimeout(() => {
if (id === 1) {
resolve({ id, value: 'sample data successfully fetched' });
} else {
throw new Error(`Failed to fetch data for ID: ${id}`);
}
}, 1000);
});
return response;
});
// 3. Dispatch the async action
console.log('--- Dispatching fetchData(1) (success) ---');
store.dispatch(fetchData(1));
// Observe state changes over time (for demonstration)
const unsubscribe = store.subscribe(() => {
console.log('Current state:', store.getState());
});
// Dispatch another action after a short delay to demonstrate error handling
setTimeout(() => {
console.log('\n--- Dispatching fetchData(2) (failure) ---');
store.dispatch(fetchData(2));
}, 3000);
// Clean up subscription after demonstration
setTimeout(() => {
unsubscribe();
console.log('\nDemonstration complete.');
}, 5000);