CreateAsyncThunk Error: Actions must be plain objects. Use custom middleware for async actions
Asked Answered
U

5

8

I am currently setting up my RTK (Redux Toolkit) and did some minor testings. Here's my code:

store/index.js

import { configureStore } from '@reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'

export default configureStore({
  reducer: {
    login: loginSliceReducer
  }
})

loginSlice.js

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import ApiService from '../../services/ApiService'

export const authorize = createAsyncThunk(
  'api/authorize',
  async (email, password) => {
    const response = await ApiService.post(email, password)
    return response.data
  }
)

export const loginSlice = createSlice({
  name: 'login',
  initialState: {
    loading: true,
    token: null,
    data: []
  },
  reducers: {
    updateState: (state, action) => {
      const { payload } = action
      switch (payload.type) {
        case AUTH_SUCCESS:
          state.loading = false
          state.token = payload.token
          state.data = payload.data
          break
        default:
      }
    }
  },
  extraReducers: {
    [authorize.fulfilled]: (state, action) => {
      // ... do state update here
    }
  }
})

export default loginSlice.reducer

login.js

import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { authorize } from './loginSlice'

const Login = () => {
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(authorize('[email protected]', 'test123'))
  }, [])

  return <div>Auth Test</div>
}

The code above doesn't work. I keep getting this error:

Error: Actions must be plain objects. Use custom middleware for async actions.

On this line: > 25 | dispatch(authorize('[email protected]', 'test123'))

Please don't mind me triggering the authorize on useEffect, as this is only a test to check if the endpoint is being called and to check if the state will update once the request is successful. :-D

Unwarrantable answered 20/7, 2020 at 5:10 Comment(3)
tagging @markerikson on my error. Please helpUnwarrantable
show us your code where you configure the store. Have you applied the thunk middleware?Cabbageworm
I'll update the postUnwarrantable
A
13

I had this same issue and it was caused by the fact that I was adding additional middleware.

@reduxjs/toolkit's configureStore has some middleware that is included by default, but if you add anything to the middleware property, it will overwrite these defaults.

The solution is to include the default middleware along with the middleware you define:

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';

import { loginSliceReducer } from './views/page/login/loginSlice';
import { otherMiddleware } from './middlewares/some-other-module';

export default configureStore({
  reducer: {
    login: loginSliceReducer
  },
  middleware: [ // Because we define the middleware property here, we need to explictly add the defaults back in.
    ...getDefaultMiddleware(),
    otherMiddleware
  ]
})

Note, there is no need to explicitly include redux-thunk when using @reduxjs/toolkit because it is already part of the default middlewares from getDefaultMiddleware()

Aphra answered 19/8, 2020 at 23:2 Comment(1)
this does not seem to be the solution to the OP question. I see how this could be valuable though for people who've accidentally overwritten their default middleware and are getting a similar error. The way RTK team suggests is to use the following middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(loggerMiddleware), redux-toolkit.js.org/api/getDefaultMiddlewareSenegambia
C
0

Looks like the problem is that you didn't add to your store a middleware capable of handling async actions

In your store/index.js try smth like:

import { applyMiddleware } from 'redux';
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'
import thunk from 'redux-thunk';

export default configureStore({
  reducer: {
    login: loginSliceReducer
  },
  middleware: [applyMiddleware(thunk), getDefaultMiddleware()]
})
Cabbageworm answered 20/7, 2020 at 6:59 Comment(5)
I am still getting the same error. By the way, here's my RTK version: - reduxjs/[email protected] - [email protected] - [email protected] - [email protected] and I have this: - UNMET PEER DEPENDENCY [email protected] - ERR! peer dep missing: react-redux@^4.0.0 || ^5.0.0 || ^6.0.0, required by [email protected]Unwarrantable
hm... maybe change the orders of middlewares so that thunk one goes first? will update my code now...Cabbageworm
sure! I'll try investigating further about this issue. Thank you so much!Unwarrantable
any further luck @Unwarrantable ?Megalocardia
redux-thunk is included with RTK by default: redux-toolkit.js.org/usage/…Senegambia
D
0

If there are more than one argument to be passed to an action creator, you must pass them inside an object. For example, if I have to send email and password as payload, I will have to send them in an object like below:

dispatch(authorize({email, password}))
Dominations answered 6/3, 2021 at 12:32 Comment(0)
S
0

one way you can fix this is to use your store's dispatch method. This way, since thunk is included in Redux Toolkit (RTK) by default, you'll have access to that - which should fix the error you're getting.

Try This:

store/index.js

import { configureStore } from '@reduxjs/toolkit'
import { loginSliceReducer } from './views/page/login/loginSlice'

export default const store = configureStore({
  reducer: {
    login: loginSliceReducer
  }
})

const useAppDispatch = () => store.dispatch

export { useAppDispatch }

login.js

import React, { useEffect } from 'react'
import { useSelector} from 'react-redux'
import { authorize } from './loginSlice'
import { useAppDispatch } from './store/index'

const Login = () => {
  const dispatch = useAppDispatch()
  useEffect(() => {
    dispatch(authorize('[email protected]', 'test123'))
  }, [])

  return <div>Auth Test</div>
}
Senegambia answered 13/4, 2021 at 17:16 Comment(0)
D
0

This helped me!

getDefaultMiddleware is useful if you want to add some custom middleware, but also still want to have the default middleware added as well:
import { configureStore } from '@reduxjs/toolkit'

import logger from 'redux-logger'

import rootReducer from './reducer'

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
})

The above solution is from official redux toolkit documentation:

https://redux-toolkit.js.org/api/getDefaultMiddleware

Dedie answered 24/3 at 7:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.