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

Viewed 11461

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 :).

7 Answers

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

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

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

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],
});

Simplest solution for me was to replace:

const dispatch = useDispatch();

with:

const dispatch = useDispatch<any>();

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

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;
Related