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
One potential issue is that this line looks wrong:
There’s three problems in that one line:
applyMiddleware
should always be firstcomposeWithDevtools()
replaces the standalonecompose
so it really should be:
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’screateAsyncThunk
will do the "dispatch actions for an async request" work for you.See our docs for details on how to use Redux Toolkit:
…what @markerikson said plus…
The
onSubmit
callback makes a call todispatch()
with the return fromregister()
as its argument. The return fromregister()
is a Promise, not a serializable object.register()
fulfills (resolves) with the return value fromdispatch()
which returns a Promise.