Home > front end >  Actions must be plain objects. Use custom middleware for async actions. Redux
Actions must be plain objects. Use custom middleware for async actions. Redux

Time:05-21

I keep getting dispatch error with redux/ typescript. Would be great if someone can help correct the setup I have here:

Store:

import {
  configureStore,
  combineReducers,
  MiddlewareArray,
} from '@reduxjs/toolkit'

import { useDispatch } from 'react-redux'

//import slice as reducer
import userReducer from './features/userSlice'
import heroReducer from './features/heroSlice'
import menuReducer from './features/menuSlice'
import utilsReducer from './features/utilsSlice'
import templatesReducer from './features/templatesSlice'

const rootReducer = combineReducers({
  //combine all reducers
  user: userReducer,
  hero: heroReducer,
  menu: menuReducer,
  utils: utilsReducer,
  templates: templatesReducer,
})


export const store = configureStore({
  reducer: rootReducer,
  middleware: new MiddlewareArray(),
})

export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()

where the dispatch is happening:

  useEffect(() => {
    const fetchData = () => {
      // TODO
      dispatch(fetchUser()).  <--- error portion
    }
    fetchData()
  }, [])

user slice:

import { createSlice, createSelector, createAsyncThunk } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

import axios from 'axios'

export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (thunkAPI) => {
    try {
      const response = await axios.get('/api/user', {
        headers: {
          'Content-Type': 'application/json',
        },
      })
      return response.data
    } catch (error) {
      // return rejectWithValue({ error: error.message })
      return console.log(error.message)
    }
  }
)

const userSlice = createSlice({
  name: 'user',
  initialState: {
    userData: {},
    isSubscribed: false,
  },
  reducers: {
    setUserData: (state: any, action: any) => {
      state.userData = action.payload.userData
    },
    setIsSubscribed: (state: any, action: any) => {
      state.isSubscribed = action.payload.isSubscribed
    },
  },
  extraReducers: (builder) => {
    builder.addCase(fetchUser.fulfilled, (state: any, action: any) => {
      state.userData = action.payload
      state.isSubscribed = action.payload.isSubscribed
    })
  },
})

export const userData = createSelector(
  (state: any) => ({
    userData: state.userData,
    isSubscribed: state.isSubscribed,
  }),
  (state: any) => state.userData
)

export const { setUserData, setIsSubscribed } = userSlice.actions
export default userSlice.reducer

Not too sure what else is needed to get rid of type errors?

Original error:

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

Type Error on dispatch(fetchUser())

'Argument of type 'AsyncThunkAction<any, void, {}>' is not assignable to parameter of type 'AnyAction''

npm ls redux shows:

enter image description here

CodePudding user response:

Your middleware: new MiddlewareArray() removes all middleware from the store - while usually a few would be included. Among them is redux-thunk, which allows for the dispatch of thunks.

Change it to:

export const store = configureStore({
  reducer: rootReducer,
})

By the way you also don't need combineReducers, so you can do

export const store = configureStore({
  reducer: {
    user: userReducer,
    hero: heroReducer,
    menu: menuReducer,
    utils: utilsReducer,
    templates: templatesReducer,
  },
})

CodePudding user response:

The issue you have here is that there is no middleware available to receive thunks, specifically the fetchUserUser thunk that you are dispatching.

The call to new MiddlewareArray() likely returns an empty middleware array which does not include the thunk middleware.

To introduce such middleware, you can do so in the following ways when configuring your store:

import thunk from 'redux-thunk'

...

export const store = configureStore({
  reducer: rootReducer,
  middleware: [thunk] // add the thunk middleware
})

OR alternatively, you may use the getDefaultMiddleware callback, which includes the same thunk middleware from 'redux-thunk' alongside other helpful serializability- and immutability-check middleware in development. Find more docs on this here.

This is what that would look like:

...
export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware() // includes the thunk middleware
})
  • Related