Actions should be pure objects. Employ specialized middleware for handling asynchronous actions in Redux

I've encountered a dispatch error while using redux with TypeScript. It would be really helpful if someone could assist me in correcting the setup I currently have:

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()).  <--- issue here
    }
    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

I'm not entirely sure what else needs to be done to resolve these 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:

https://i.sstatic.net/VkOY1.png

Answer №1

When utilizing the

middleware: new MiddlewareArray()
, keep in mind that it clears out all middleware from the store, even though typically a few are included by default. One such essential middleware is redux-thunk, which enables the dispatch of thunks.

Consider the following approach:

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

Additionally, there's no need for combineReducers, so you can simplify it like this:

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

Answer №2

The issue at hand is the absence of middleware capable of handling thunks, particularly the fetchUserUser thunk that you are dispatching.

It seems that calling new MiddlewareArray() is returning an empty middleware array that does not incorporate the required thunk middleware.

To address this challenge, there are a couple of options for configuring your store to include the necessary middleware:

import thunk from 'redux-thunk'

...

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

Alternatively, you can utilize the getDefaultMiddleware callback, which includes the essential thunk middleware from 'redux-thunk' alongside other helpful serialization- and immutability-checking middleware during development. Additional documentation can be found here.

This is how it would be implemented:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing Material-UI with MobileDialog HOC in TypeScript: A Beginner's Guide

I'm running into an issue while trying to implement withMobileDialog in my TypeScript code. Below is the snippet of my code, inspired by a code example from the official documentation. import withMobileDialog, { InjectedProps } from "@material-ui/co ...

When submitting in an Angular mat-dialog, the page should refresh without closing the dialog

I recently started working with Angular and had to retrieve data from the database to populate a user grid. I successfully completed that task and then moved on to using MatDialog for creating new users. After fixing the creation services and linking them ...

Building a BaseObserver in TypeScript with RxJS

Initially, I created a class called BaseObserver in Swift. In the subscribe method, I pass this class. Now, I am attempting to achieve the same functionality in RxJS using TypeScript. This approach proves useful when you need to execute actions both befor ...

When adding new elements to an array, the IDs of all objects become identical

When running the code below: dietDay.meals.forEach((meal) => { meal.mealProducts.forEach((mealProduct) => { if ( mealProduct.product.id && this.fetchedProductIds.includes(mealProduct.p ...

Waiting for asynchronous subscriptions with RxJS Subjects in TypeScript is essential for handling data streams efficiently

Imagine having two completely separate sections of code in two unrelated classes that are both listening to the same Observable from a service class. class MyService { private readonly subject = new Subject<any>(); public observe(): Observable&l ...

Struggling to successfully pass a function as an argument to the setTimeout method within an array in node.js using TypeScript

Here is an example that successfully demonstrates a function being called using setTimeout: function displayMessage(msg: string){ console.log(msg); } setTimeout(displayMessage, 1000, ["Hi!"]; After one second, it will print out "Hi!" to the console. ...

The 'input' element does not recognize the property 'formControl', causing a binding issue in Angular Material Autocomplete with Angular 12

Recently, I upgraded my Angular app from version 11 to 12 along with all the dependencies, including Angular Material. However, after running 'ng serve', I encountered the following error: Error: src/app/components/chips/chips.component.html:19:1 ...

What could be causing Angular to replace the original variable?

As a newcomer to Angular, I've been working on this code for hours now. Hopefully, it will all come together for someone out there who can make sense of it. export class QuizComponent implements OnInit { originalArray: IArray[] = []; tempArray: I ...

Redux - The same reducers, containers, and components are yielding varying outcomes

update: Issue resolved by connecting a different variable to the mapStateToProps. I'm encountering some challenges with my react-redux application and I'm struggling to pinpoint the error in my setup. You can access the source code here. The f ...

Sharing markdown content between two Vue.js components

I have a markdown editor in View A which is displaying the result in the current View. My goal is to share this result with another page, View B. In View A, there is a button that allows the user to share the markdown result with View B. I am using a texta ...

I'm having trouble viewing the unique Google Map design on my application

I have recently customized Google maps following the guidelines in this documentation: https://developers.google.com/maps/documentation/javascript/styling For styling, I utilized the Cloud tool and opted for the available template instead of using JSON st ...

Unable to locate the necessary file. - Implementing TypeScript in a React application

Attempting to integrate TypeScript into an existing React app by following the steps outlined at: https://create-react-app.dev/docs/adding-typescript I've followed all the instructions but encountered the following error upon trying to launch the app ...

Encountering crashes while initializing the router in the constructor of a service in Angular 4.3

I've been scratching my head over this problem. It seems like I'm overlooking something simple. Let me show you what's inside my home.component.ts file: import { Component, OnInit } from '@angular/core'; import { AuthService } f ...

Developing the headers for a service using React.js

As someone new to ReactJs, I'm curious about the various methods we can use to include Headers in our service Url before making a call. While I'm familiar with how GET/POST Calls are made in angular Js after including headers, I'd like to l ...

Error in NextJS: The name 'NextApplicationPage' cannot be found

const { Component, pageProps}: { Component: NextApplicationPage; pageProps: any } = props After implementing the code above with 'Component' type set to NextApplicationPage, an error message pops up stating, The name 'NextApplicationPage&ap ...

How can I display an iframe element only if it exists within an object in Angular?

I'm currently exploring options to specifically display the iframe element within a block of HTML content being sent to my Angular application. While I can use a *ngIf directive in my template to check for the presence of the tag, I am unsure of how ...

Angular 8: Issue with PatchValue in Conjunction with ChangeDetector and UpdateValue

I am puzzled by the fact that PatchValue does not seem to work properly with FormBuilder. While it shows data when retrieving the value, it fails to set it in the FormBuilder. Does anyone have an idea why this might be happening? I am utilizing UpdateValue ...

Having trouble establishing a connection with Db2 while using protractor

Encountering an issue when attempting to establish a connection with a remote DB2 database, resulting in the following exception: SQL30081N A communication error has been detected. The communication protocol in use is 'TCP/IP'. The com ...

What are some ways to optimize the performance of a Select Box?

I am attempting to show a lengthy list of countries in an ion-select. Currently, there are 249 countries that I need to load. Unfortunately, the rendering performance is quite slow on my phone. <ion-list margin-top margin-bottom> <ion-item> ...

Displaying error messages in Angular Material upon clicking a button

I am struggling with validation using the <mat-form-field> and <mat-error>. The validation works as expected when the user tabs out of the input without filling it. However, I'm facing a challenge in making this error show up when a button ...