36

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

Lun
  • 861
  • 1
  • 10
  • 18
  • In my case I just updated redux, react-redux, and reduxjs-toolkit versions on package.json, deleted node_modules folder, and ran yarn install. – Alcides Bezerra May 27 '22 at 13:13
  • 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-typescript` – Wayne Mao Sep 07 '22 at 02:36
  • @WayneMao care to elaborate on that comment? – Yanick Rochon Mar 03 '23 at 02:43

12 Answers12

101

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

wasilikoslow
  • 1,411
  • 1
  • 9
  • 13
  • 9
    From where do you import `AppDispatch`? – Rafayel Jul 24 '22 at 17:08
  • 9
    @Rafayel https://redux-toolkit.js.org/tutorials/typescript#define-root-state-and-dispatch-types – wasilikoslow Jul 25 '22 at 16:43
  • You can set the AppDispatch type in your store file, as described in the docs [here](https://redux-toolkit.js.org/usage/usage-with-typescript#getting-the-dispatch-type). tldr: `export type AppDispatch = typeof store.dispatch` – IrrerPolterer Aug 15 '23 at 09:15
17

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

Aditya
  • 699
  • 6
  • 9
13

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,
});
awdyson
  • 243
  • 3
  • 12
11

This worked for me

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

const dispatch = useDispatch<ThunkDispatch<any, any, any>>();
Jim Doyle
  • 2,302
  • 1
  • 16
  • 12
9

Simplest solution for me was to replace:

const dispatch = useDispatch();

with:

const dispatch = useDispatch<any>();
Jos
  • 1,015
  • 2
  • 13
  • 25
  • 9
    This is the worst solution I have ever seen. We use typescript to have types instead of use any – Sharpek Sep 03 '22 at 08:56
6

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],
});
Terminat
  • 1,199
  • 5
  • 21
  • I added the middleware, but for some reason I'm still getting the same error D: – Lun Nov 28 '21 at 14:17
  • So... it's working but vs-code is not happy about it, guess I'll have to deal with it. – Lun Nov 28 '21 at 15:28
  • 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? – Terminat Nov 28 '21 at 20:28
  • 4
    Thunk is added automatically on `configureStore` call - this approach does not add thunk, but actually removes other middlewares. Please don't do that. – phry Nov 28 '21 at 21:31
3

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

phry
  • 35,762
  • 5
  • 67
  • 81
1

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.

andrewnosov
  • 374
  • 4
  • 14
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.

Hareesh
  • 1,507
  • 3
  • 21
  • 43
1

I figured out for my case, what if you use configureStore from '@reduxjs/toolkit' and want to use middlewares, then you can't write somethink 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

lomobit
  • 26
  • 4
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.

Archer
  • 11
  • 5