0
0
ReactDebug / FixBeginner · 4 min read

How to Handle Async in Redux: Fix and Best Practices

To handle async operations in Redux, use middleware like redux-thunk or redux-saga that lets you write async logic inside action creators. This avoids dispatching plain objects only and allows dispatching functions that perform async calls and then dispatch actions with results.
🔍

Why This Happens

Redux by default expects actions to be plain objects. When you try to dispatch a function or a promise directly for async work, Redux throws an error or ignores it because it can't handle async code on its own.

javascript
import { createStore } from 'redux';

const reducer = (state = { data: null }, action) => {
  switch (action.type) {
    case 'SET_DATA':
      return { ...state, data: action.payload };
    default:
      return state;
  }
};

const store = createStore(reducer);

// Async action creator returning a function (not supported by default)
const fetchData = () => {
  return (dispatch) => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => dispatch({ type: 'SET_DATA', payload: data }));
  };
};

store.dispatch(fetchData());
Output
Error: Actions must be plain objects. Use custom middleware for async actions.
🔧

The Fix

Use middleware like redux-thunk that allows dispatching functions. This middleware intercepts function actions, runs the async code inside, and dispatches plain object actions when ready.

javascript
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const reducer = (state = { data: null }, action) => {
  switch (action.type) {
    case 'SET_DATA':
      return { ...state, data: action.payload };
    default:
      return state;
  }
};

const store = createStore(reducer, applyMiddleware(thunk));

const fetchData = () => {
  return async (dispatch) => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    dispatch({ type: 'SET_DATA', payload: data });
  };
};

store.dispatch(fetchData());
Output
State updates with fetched data after async call completes.
🛡️

Prevention

Always use middleware like redux-thunk or redux-saga for async logic in Redux. Keep action creators pure by dispatching plain objects only after async calls. Use linting rules to warn if non-plain objects are dispatched. Structure async code clearly to avoid side effects in reducers.

⚠️

Related Errors

Common related errors include dispatching promises directly, which Redux also rejects, or trying to perform async calls inside reducers, which must be pure and synchronous. The fix is to keep async logic in middleware or action creators and reducers pure.

Key Takeaways

Redux requires middleware like redux-thunk to handle async actions properly.
Never dispatch functions or promises directly without middleware; Redux expects plain objects.
Keep reducers pure and synchronous; put async logic in action creators or middleware.
Use async/await inside thunk action creators for clear and readable async code.
Lint your code to catch improper dispatches early and maintain clean Redux patterns.