skip to Main Content

I am trying to figure out why I am getting "Actions must be plain objects. Use custom middleware for async actions." when I dispatch my thunk action. From my understanding, the normal cause of this issue would be that my thunk middleware wasn’t applied, and thus redux only expects POJO actions, instead of thunk actions. However, unless I am mistaken, I believe I have already done that. I also double checked to make sure that my thunk action creator was, in fact, returning a thunk action. I have looked around, and even asked chatgpt, and I can’t seem to find any solution to this issue. Here is the code:

applying thunk

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';

import rootReducer from './services/reducers/root_reducer.js';

// apply logger middleware only in dev env
const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));

const configureStore = (preloadedState = {}) => {
  return createStore(rootReducer, preloadedState, composedEnhancer);
};

export default configureStore;

index.js

import './assets/styles/reset.css';
import './assets/styles/index.css';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from './store.js';
import App from './app.js';

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(
  <Router>
    <Provider store={configureStore()}>
      <App />
    </Provider>
  </Router>
)

thunk action

import UserApi from '../api/user_api.js';
import { UserActions } from '../constants/actions.js';
import { displayError } from './error_actions.js';
import userPool from '../../user_pool.js';

export const addUser = (user) => {
  return {
    type: UserActions.addUser,
    payload: user
  };
};

export const register = (registerData) => {
  return async (dispatch) => {
    userPool.signUp(registerData.email, registerData.password, registerData.attributeList, null, (error, result) => {
      if (error) {
        return dispatch(displayError(error));
      } else {
        return dispatch(addUser(result.user));
      }
    });
  }
}

dispatching thunk action

  // Called when form is submitted
  const onSubmit = (event) => {
    event.preventDefault();
    const registerParams = {
      email: user.email,
      password: user.password,
      attributeList: []
    };
    delete user.email;
    delete user.password;
    Object.entries(user).forEach(([key, value]) => {
      if (key === "birthdate") {
        const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
        value = value.toLocaleDateString('en-US', options).replace(///g, '-');
      }
      let attributeParams = {
        Name: key,
        Value: value
      };
      let cognitoAttribute = new CognitoUserAttribute(attributeParams);
      registerParams.attributeList.push(cognitoAttribute);
    });
    dispatch(register(registerParams));
  };

2

Answers


  1. One potential issue is that this line looks wrong:

    const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));
    

    There’s three problems in that one line:

    • applyMiddleware should always be first
    • if you’re adding the devtools setup it should be last
    • but actually composeWithDevtools() replaces the standalone compose

    so it really should be:

    const composedEnhancer = composeWithDevtools(applyMiddleware(thunk, logger.default));
    

    I’m not certain that is the cause of the error, but there’s a good chance.

    HOWEVER, there’s another issue:

    The style of Redux logic you’re writing is very outdated.

    Today, all Redux users should be using our official Redux Toolkit package to write their Redux code. It drastically simplifies Redux usage, and that includes some of the code in your example.

    In particular, RTK’s configureStore API sets up the store with the thunk middleware and Redux DevTools already configured, and RTK’s createAsyncThunk will do the "dispatch actions for an async request" work for you.

    See our docs for details on how to use Redux Toolkit:

    Login or Signup to reply.
  2. …what @markerikson said plus…

    The onSubmit callback makes a call to dispatch() with the return from register() as its argument. The return from register() is a Promise, not a serializable object.

    register() fulfills (resolves) with the return value from dispatch() which returns a Promise.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search