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

15

49

store.ts

export const store = configureStore({
    reducer: {
        auth: authReducer
    },
    middleware: [],
});

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

hooks.ts

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

authSlice.ts (function that causes the problem)

export const fetchUser = createAsyncThunk(
    'users/fetchByTok',
    async () => {
        const res = await getUser();
        return res.data;
    }
)

Auth.ts

const Auth = ({ component, isLogged }: {component: any, isLogged: boolean}) => {
    const dispatch = useAppDispatch();
    
    useEffect(() => {
        dispatch(fetchUser()) // <----------- ERROR
    }, []);

    return isLogged ? component : <Navigate to='/sign-in' replace={true} />;
}

export default Auth;

I have a createAsyncThunk function that fetches the user, but I cannot actually put it in the dispatch()...

  • Argument of type 'AsyncThunkAction<any, void, {}>' is not assignable to parameter of type 'AnyAction'.
  • Property 'type' is missing in type 'AsyncThunkAction<any, void, {}>' but required in type 'AnyAction'.ts(2345)

First time using this, so a nice explanation would be nice :).

Endolymph answered 28/11, 2021 at 13:21 Comment(3)
In my case I just updated redux, react-redux, and reduxjs-toolkit versions on package.json, deleted node_modules folder, and ran yarn install.Lallation
I met the same issue and comes here, but all the answers doesn't solve my problem. finally I resolved the issue by comparing to the template project created by CRA using redux-typescript template, checking the types step by step. you can have a try as well: npx create-react-app my-app --template redux-typescriptPooley
@WayneMao care to elaborate on that comment?Mislead
W
8

EDIT!

Since as mentioned in the comment, redux toolkit actually adds Thunk by default, the answer from Phry is more accurate. I can't delete the accepted answer, so this edit would have to suffice.

The answer that I provided will remove other middlewares that are automatically added!

The problem is that you're actually missing the thunk middleware inside store configuration. Just add an import for thunkMiddleware and add it in the middleware array in your configuration. Since the middleware is not added, the dispatch won't accept the Thunk Action, because it is not supported by redux out of a box.

import thunkMiddleware from 'redux-thunk';

export const store = configureStore({
    reducer: {
        auth: authReducer
    },
    middleware: [thunkMiddleware],
});
Wraith answered 28/11, 2021 at 14:7 Comment(4)
I added the middleware, but for some reason I'm still getting the same error D:Endolymph
So... it's working but vs-code is not happy about it, guess I'll have to deal with it.Endolymph
Hmm I would suspect it is more of a vs code problem, maybe restarting it would help? Or checking if vs code is using proper typescript version?Wraith
Thunk is added automatically on configureStore call - this approach does not add thunk, but actually removes other middlewares. Please don't do that.Jacks
G
132

I faced the same issue, for me it was just solved by adding AppDispatch to the type of useDispatch hook;

 const dispatch = useDispatch<AppDispatch>();

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

getUsers() was my createAsyncThunk function

Gavial answered 29/4, 2022 at 14:11 Comment(3)
From where do you import AppDispatch?Axil
@Axil redux-toolkit.js.org/tutorials/…Gavial
You can set the AppDispatch type in your store file, as described in the docs here. tldr: export type AppDispatch = typeof store.dispatchCorkscrew
D
21

The rest of the answers suggest updating the type of store.dispatch by inference, which I too prefer.

Here, I want to suggest an alternative using explicit type definitions if, for some reason, you fail to solve it through inference (which can happen in larger projects, etc.)

So the idea here is to explicitly define the type of your redux store with an updated dispatch type which supports thunk actions.

Solution using Explicit type declaration


// your redux store config file.
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import reducers from "./path/to/reducers";

// ... your code 

// 1. Get the root state's type from reducers
export type RootState = ReturnType<typeof reducers>;

// 2. Create a type for thunk dispatch
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;

// 3. Create a type for store using RootState and Thunk enabled dispatch
export type AppStore = Omit<Store<RootState, AnyAction>, "dispatch"> & {
  dispatch: AppThunkDispatch;
};

//4. create the store with your custom AppStore
export const store: AppStore = configureStore();

// you can also create some redux hooks using the above explicit types
export const useAppDispatch = () => useDispatch<AppThunkDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Using the above method you can also use store.dispatch to dispatch the async thunk actions (might be helpful when you are writing tests)

or use the useAppDispatch hook

or use the old school connect consumer function from react-redux.

It will give you correct types in all the cases.

I personally prefer inference-type declarations most of the time, but sometimes we don't have a choice because of external or internal reasons.

I hope this answer is helpful. Thank you :)

Depside answered 3/8, 2022 at 6:20 Comment(0)
H
19

This worked for me

import {ThunkDispatch} from "@reduxjs/toolkit";

const dispatch = useDispatch<ThunkDispatch<any, any, any>>();
Hydrodynamic answered 20/12, 2022 at 1:59 Comment(0)
O
15

For me the solution was to stick more closely to the RTK documentation example.

So using concat...

const store = configureStore({
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(FooApi.middleware, apiErrorMiddleware),
  ...rest_of_the_config,
});

...instead of spreading the array...

const store = configureStore({
  middleware: (getDefaultMiddleware) =>
    [...getDefaultMiddleware(), FooApi.middleware, apiErrorMiddleware],
  ...rest_of_the_config,
});
Oxidase answered 1/6, 2022 at 22:12 Comment(0)
C
13

Simplest solution for me was to replace:

const dispatch = useDispatch();

with:

const dispatch = useDispatch<any>();
Conchology answered 5/8, 2022 at 12:24 Comment(2)
This is the worst solution I have ever seen. We use typescript to have types instead of use anyConsumedly
what's the point of using typescript is you want to use any?Asshur
W
8

EDIT!

Since as mentioned in the comment, redux toolkit actually adds Thunk by default, the answer from Phry is more accurate. I can't delete the accepted answer, so this edit would have to suffice.

The answer that I provided will remove other middlewares that are automatically added!

The problem is that you're actually missing the thunk middleware inside store configuration. Just add an import for thunkMiddleware and add it in the middleware array in your configuration. Since the middleware is not added, the dispatch won't accept the Thunk Action, because it is not supported by redux out of a box.

import thunkMiddleware from 'redux-thunk';

export const store = configureStore({
    reducer: {
        auth: authReducer
    },
    middleware: [thunkMiddleware],
});
Wraith answered 28/11, 2021 at 14:7 Comment(4)
I added the middleware, but for some reason I'm still getting the same error D:Endolymph
So... it's working but vs-code is not happy about it, guess I'll have to deal with it.Endolymph
Hmm I would suspect it is more of a vs code problem, maybe restarting it would help? Or checking if vs code is using proper typescript version?Wraith
Thunk is added automatically on configureStore call - this approach does not add thunk, but actually removes other middlewares. Please don't do that.Jacks
C
5

import * as reduxThunk from "redux-thunk/extend-redux";

Try adding this import statement in redux store configuration file or root file - app.js/index.js or in case of next js - _app.js in pages directory.

eg : store.js

import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./reducers";
import * as reduxThunk from "redux-thunk/extend-redux";

export const store = configureStore({
    reducer: rootReducer,
});
export default store;
Cosmetician answered 20/9, 2022 at 11:40 Comment(0)
O
2

I figured out for my case, what if you use configureStore from '@reduxjs/toolkit' and want to use middlewares, then you can't write something like this:

export const store = configureStore({
    reducer: {
        r1: r1Reducer,
    },
    devTools: true,
    middleware: [
       reduxLogger
    ],
});

instead of this you should use this:

 middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(reduxLogger),

or use answer by @andrewnosov

Odysseus answered 4/3, 2023 at 10:34 Comment(1)
Yup, working for meMalaguena
J
1

There is a common TS issue that surfaces like this if you have redux in the versions 4.0.5 and 4.1.x both somewhere in your node_modules.

For many people, uninstalling and re-installing react-redux or @types/react-redux seems to solve the problem.

Otherwise your bundler might help you find the source of that problem (npm ls redux or yarn why redux if you are using one of those two).

Jacks answered 28/11, 2021 at 21:34 Comment(0)
D
1

If you do not have any middlewares, don't add it as empty array, just remove it.

If you have some middlewares you have to merge it with default middleware using concat method like

middleware: getDefaultMiddleware => getDefaultMiddleware().concat(yourMiddleware)

Never use spread operator to merge the middlewares. Spread operator will not retain the types but concat method does.

Dismissive answered 6/1, 2023 at 13:50 Comment(0)
M
1

Here's what the Usage with TypeScript doc suggest that eventually works for me:

In store.ts

export type AppDispatch = typeof store.dispatch

In hooks.ts

export const useAppDispatch: () => AppDispatch = useDispatch

In yourComponent.tsx

const dispatch = useAppDispatch()
dispatch(yourThunkAction)
Mcculloch answered 21/9, 2023 at 2:24 Comment(0)
S
1

Just follow the Redux Toolkit docs for using useDispatch and useSelector in TypeScript.

Firstly, export RootState and AppDispatch from your store.ts file:

import { configureStore } from '@reduxjs/toolkit'
// ...

export const store = configureStore({
  reducer: {
    posts: postsReducer,
    comments: commentsReducer,
    users: usersReducer,
  },
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

Then create a hooks.ts file to create typed version of useDispatch and useSelector hooks, this basically saves you from extra typing:

import { useDispatch, useSelector } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
import type { RootState, AppDispatch } from './store'

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

Now you can simply import useAppDispatch and useAppSelector which are the typed version of useDispatch and useSelector from your hooks.ts file:

import {useAppDispatch, useAppSelector} from "app/hooks";
// ...

export const App = () => {
    const dispatch = useAppDispatch();
    const state = useAppSelector((state) => state);
}
Scoundrel answered 20/10, 2023 at 9:36 Comment(0)
C
1

in my case, After adding <any> issues was resolved.

const dispatch = useDispatch<any>();
useEffect(() => {
    dispatch(fetchProducts());
}, [])
Clarey answered 3/12, 2023 at 17:43 Comment(0)
C
0

You've replaced default middlewares with empty array in your configuration of store. So, your app don't have redux-thunk.

You can do something like:

const middleware = [
  ...getDefaultMiddleware(),
  /* custom middlewares */
];

and then add this array to configuration object.

Coshow answered 7/10, 2022 at 14:20 Comment(0)
U
0

For me it was a problem with typescript, adding createAsyncThunk() types according to docs solved the issue. more info on: https://redux-toolkit.js.org/api/createAsyncThunk especially the last parts.

Ullage answered 15/1, 2023 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.