Source:  Twitter logo

Unhandled Rejection (Error): Actions must be plain objects. Use custom middleware for async actions.

I wanted to add comments with every posts. So when fetch posts are run I want to call fetch comment API for all post.

export function bindComments(postId) {
  return API.fetchComments(postId).then(comments => {
    return {
      type: BIND_COMMENTS,
      comments,
      postId
    }
  })
}

You have to dispatch after the async request ends.

This would work:

export function bindComments(postId) {
    return function(dispatch) {
        return API.fetchComments(postId).then(comments => {
            // dispatch
            dispatch({
                type: BIND_COMMENTS,
                comments,
                postId
            });
        });
    };
}
85 users liked answer #0dislike answer #085
sadiq profile pic
sadiq

For future seekers who might have dropped simple details like me, in my case I just have forgotten to call my action function with parentheses.

actions.js:

export function addNewComponent() {
  return {
    type: ADD_NEW_COMPONENT,
  };
}

myComponent.js:

import React, { useEffect } from 'react';
import { addNewComponent } from '../../redux/actions';

  useEffect(() => {
    dispatch(refreshAllComponents); // <= Here was what I've missed.
  }, []);

I've forgotten to dispatch the action function with (). So doing this solved my issue.

  useEffect(() => {
    dispatch(refreshAllComponents());
  }, []);

Again this might have nothing to do with OP's problem, but I hope I helps people with the same problem as mine.

43 users liked answer #1dislike answer #143
Alex Jolig profile pic
Alex Jolig

The error is simply asking you to insert a Middleware in between which would help to handle async operations.

You could do that by :

npm i redux-thunk

        Inside index.js

import thunk from "redux-thunk" 
import { createStore, applyMiddleware } from 'redux';
        
...createStore(rootReducers, applyMiddleware(thunk));

Now, async operations will work inside your functions.

29 users liked answer #2dislike answer #229
Ashutosh Tiwari profile pic
Ashutosh Tiwari

You can't use fetch in actions without middleware. Actions must be plain objects. You can use a middleware like redux-thunk or redux-saga to do fetch and then dispatch another action.

Here is an example of async action using redux-thunk middleware.

export function checkUserLoggedIn (authCode) {
 let url = `${loginUrl}validate?auth_code=${authCode}`;
  return dispatch => {
    return fetch(url,{
      method: 'GET',
      headers: {
        "Content-Type": "application/json"
      }
      }
    )
      .then((resp) => {
        let json = resp.json();
       if (resp.status >= 200 && resp.status < 300) {
          return json;
        } else {
          return json.then(Promise.reject.bind(Promise));
        }
      })
      .then(
        json => {
          if (json.result && (json.result.status === 'error')) {
            dispatch(errorOccurred(json.result));
            dispatch(logOut());
          }
          else{
            dispatch(verified(json.result));
          }
        }
      )
      .catch((error) => {
        dispatch(warningOccurred(error, url));
      })
  }
}
14 users liked answer #3dislike answer #314
Sinapcs profile pic
Sinapcs

Make use of Arrow functions it improves the readability of code. No need to return anything in API.fetchComments, Api call is asynchronous when the request is completed then will get the response, there you have to just dispatch type and data.

Below code does the same job by making use of Arrow functions.

export const bindComments = postId => {
  return dispatch => {
    API.fetchComments(postId).then(comments => {
      dispatch({
        type: BIND_COMMENTS,
        comments,
        postId
      });
    });
  };
};
7 users liked answer #4dislike answer #47
Amruth profile pic
Amruth

Change:

export const <youractionName> = async (dispatch) => {}

to,

export const <youractionName> = () => async (dispatch) => {}

This fixed my issue. Missed a '() =>'

7 users liked answer #5dislike answer #57
Vidarshan Adithya profile pic
Vidarshan Adithya

I had same issue as I had missed adding composeEnhancers. Once this is setup then you can take a look into action creators. You get this error when this is not setup as well.

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunk))
);
2 users liked answer #6dislike answer #62
Sandeep Amarnath profile pic
Sandeep Amarnath

Use redux-thunk, setup with redux & create action like this

export const actionName = (data) => dispatch => {
  dispatch({
    type:"ACTION_TYPE"
    payload:"my payload"
  })
}
1 users liked answer #7dislike answer #71
Spandan Joshi profile pic
Spandan Joshi

You might also have forgotten to getDefaultMiddleware() in the middlewares' array, as I did. No further installations required:

export const store = configureStore({
  reducer: GlobalReducer,
  middleware: (getDefaultMiddleware) => [
    ...getDefaultMiddleware(),
    mainMiddleware,
  ],
});
1 users liked answer #8dislike answer #81
Bibbi Torres profile pic
Bibbi Torres

Action Definition

const selectSlice = () => {
  return {
    type: 'SELECT_SLICE'
  }
};

Action Dispatch

store.dispatch({
  type:'SELECT_SLICE'
});

Make sure the object structure of action defined is same as action dispatched. In my case, while dispatching action, type was not assigned to property type.

0 users liked answer #9dislike answer #90
Yuvraj Patil profile pic
Yuvraj Patil

If you are working with redux-observable check that your action returns an observable. I had the issue because I used map and not a mergemap

// error
    export const myEpic = (action$: any) =>
      action$.pipe(
        ofType('...'),
        map((x => x.payload),
        map((x) => callAPi(x)),
      )
    
// success
        export const myEpic = (action$: any) =>
          action$.pipe(
            ofType('...'),
            map((x => x.payload),
            mergeMap((x) => callAPi(x)),
          )
0 users liked answer #10dislike answer #100
corogenda software profile pic
corogenda software

Just here to share my case. I had a setLoading action, while also having

const [loading, setLoading] = useState(false)

above which I didn't delete. So it was basically not dispatching the setLoading from redux but the one from useState. Deleting/renaming this solves the problem.

0 users liked answer #11dislike answer #110
Allen Hu profile pic
Allen Hu

if things were working with this code and this is a new iteration, check to make sure you have your variables in the correct order for the function (this was my mistake)

i.e. code that got this error

export const fetchProjects = newPage => (getState, dispatch) => NOPE

export const fetchProjects = newPage => (dispatch, getState) => OK YEAH
0 users liked answer #12dislike answer #120
abischolz profile pic
abischolz

In my case, I just wanted to sent some values to the server without saving them to redux store, so I was not using a type, nor dispatching anything at the end. But I was calling the action with dispatch. So all I had to do, was to remove the dispatch, because it wasn't really an action. It was just a function.

0 users liked answer #13dislike answer #130
Fotios Tsakiris profile pic
Fotios Tsakiris

For me, the solution was to add redux-thunk as a middleware, so inside my store configuration file, I passed redux-thunk as middleware.

inside the console:

import reducerData from './store-reducer';
import {applyMiddleware, compose, createStore} from 'redux';
import ReduxThunk from 'redux-thunk';

const middlewares = [ReduxThunk];

const store = createStore(
  reducerData,
  compose(applyMiddleware(...middlewares)),
);
export default store;
0 users liked answer #14dislike answer #140
CodingEra profile pic
CodingEra

Arrow function syntax

export const bindComments = (postId) => dispatch => {
 return API.fetchComments(postId).then(comments => {
   // dispatch
    dispatch({
      type: BIND_COMMENTS,
       comments,
       postId
   })
})}
0 users liked answer #15dislike answer #150
Nazmul profile pic
Nazmul

This error occurs mainly if you are dispatching an action and your action is not returning an object. For example here is an increment function which I use it to increment number value when increment button is clicked. const increment = () => type: INCREMENT and here is my dispatch function onClick={() => dispatch(increment)} because of ommiting parenthesis () inside dispatch function now in your terminal there would be the same error appears. The reason dispatch function expects an object not a function name...

0 users liked answer #16dislike answer #160
Alijon Jumanazarov profile pic
Alijon Jumanazarov

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).