Lun
Lun

Reputation: 1131

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

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

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

Upvotes: 56

Views: 71074

Answers (16)

codeLiu
codeLiu

Reputation: 19

// src/store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import recommendReducer from './modules/recommend.ts'

const store = configureStore({
  reducer: {
    recommend: recommendReducer
  }
})

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

export default store
// src/hooks/app.ts
import type { AppDispatch, RootState } from '@/store'
import type { TypedUseSelectorHook } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'

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

I repackaged the useDispatch hooks function based on the store type of my project according to the official documentation of redux. When using dispatch, just use the encapsulated one. This way, there is ts type support.

Upvotes: 1

Saptarshi Dey
Saptarshi Dey

Reputation: 195

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

const dispatch = useDispatch<any>();
useEffect(() => {
    dispatch(fetchProducts());
}, [])

Upvotes: 1

lomobit
lomobit

Reputation: 36

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

Upvotes: 2

SamuraiSam7
SamuraiSam7

Reputation: 11

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

Upvotes: 1

TKforce
TKforce

Reputation: 624

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)

Upvotes: 2

Archer
Archer

Reputation: 21

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.

Upvotes: 0

Hareesh
Hareesh

Reputation: 1577

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.

Upvotes: 1

Jim Doyle
Jim Doyle

Reputation: 2648

This worked for me

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

const dispatch = useDispatch<ThunkDispatch<any, any, any>>();

Upvotes: 19

andrewnosov
andrewnosov

Reputation: 403

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.

Upvotes: 0

Pranav Premarajan
Pranav Premarajan

Reputation: 471

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;

Upvotes: 6

Jos
Jos

Reputation: 1089

Simplest solution for me was to replace:

const dispatch = useDispatch();

with:

const dispatch = useDispatch<any>();

Upvotes: 13

Aditya
Aditya

Reputation: 819

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

Upvotes: 23

awdyson
awdyson

Reputation: 363

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

Upvotes: 15

wasilikoslow
wasilikoslow

Reputation: 1963

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

Upvotes: 151

Terminat
Terminat

Reputation: 1237

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

Upvotes: 8

phry
phry

Reputation: 44078

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

Upvotes: 1

Related Questions