BenBorla
BenBorla

Reputation: 81

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

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

Upvotes: 8

Views: 10079

Answers (5)

Abhishek
Abhishek

Reputation: 1120

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

Upvotes: 0

tylerlindell
tylerlindell

Reputation: 1563

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>
}

Upvotes: 0

HKS
HKS

Reputation: 983

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}))

Upvotes: 0

Daniel
Daniel

Reputation: 9464

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()

Upvotes: 13

Alex Shchur
Alex Shchur

Reputation: 751

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()]
})

Upvotes: 0

Related Questions