Search code examples
reduxreact-reduxredux-thunkredux-toolkitthunk

Improper type signature for `dispatch` method despite using "app dispatch" hook


Introduction

I'm using Redux Toolkit to add Redux support to a React application served by a Django app. We're using Typescript, and so we're following the Typescript Quick Start from the Redux Toolkit docs.

An important element of using Redux (Toolkit?) with Typescript is to ensure that your dispatch(...) method has the correct type signature. As described in the docs, if you simply use the useDispatch(...) hook from react-redux,

the default Dispatch type does not know about thunks. In order to correctly dispatch thunks, you need to use the specific customized AppDispatch type from the store that includes the thunk middleware types, and use that with useDispatch. Adding a pre-typed useDispatch hook keeps you from forgetting to import AppDispatch where it's needed.

That's fine, and I followed those instructions. My code exports a new hook, just like the tutorial:

export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();

My problem is: when I use that hook, the type signature for the dispatch(...) method still doesn't accept thunks. The following code:

const dispatch = useAppDispatch();
dispatch(customAction({ ... params ... }));

Produces a compilation error:

ERROR in [at-loader] ./src/components/Widget.tsx:45:9 
TS2345: Argument of type 'AsyncThunkAction<void, CustomActionParams, {}>' is not assignable to parameter of type 'AnyAction'.
Property 'type' is missing in type 'AsyncThunkAction<void, CustomActionParams, {}>' but required in type 'AnyAction'.

My question: Why isn't the typed dispatch method correctly accepting AsyncThunkActions?

Things I've Tried

Interestingly, calling dispatch(...) with the AppDispatch type explicitly parameterized results in the same error. Only parameterizing dispatch(...) with any silences the error, which obviously isn't ideal.

const dispatch = useAppDispatch();
dispatch<AppDispatch>(customAction({ ... params ... })); // same error
dispatch<any>(customAction({ ... params ... })); // error silenced

This makes me think that the issue is AppDispatch not correctly inferring that it should take AsyncThunkAction types, which should happen automatically. Something that could be causing this is that my async action is defined within extraReducers, like so:

export const customAction = createAsyncThunk(
    "slice/customAction",
    async (payload: CustomActionParams) { ... }
);

export const slice = createSlice({
    name: "slice",
    initialState,
    reducers: {},
    extraReducers: (builder) => {
         builder.addCase(customAction.fulfilled, (state, action) => { ... });
    }
});

I'm not sure why this wouldn't cause the type to update, but it's something I've got to resolve.

Differences from standard setup

A few things about my environment could also theoretically contribute to this issue, so I'm including them here for correctness:

  • This React application is not a single page application, but is embedded in a rendered Django template by using django-react-templatetags. This library automatically injects script tags inline to pass component props and initialize components using ReactDOM. See this page for an example on how this works.
    • This means that each component has its own <Provider> component, instead of one <Provider> at the root, but all share the same store.
  • The useAppDispatch(...) custom method is defined in my store file (store.ts) rather than a hooks.ts at the root, but that shouldn't cause any issues.
  • Edit: All of this is in a Yarn 2 Workspace, rather than the root directory of a package.

Questions that are not duplicates:

  • This question uses an incorrect definition for useAppDispatch(...). I'm using the correct definition in my code.
  • This question doesn't use a custom dispatch hook.
  • This question uses incorrect and overspecified types for the dispatch method of the thunkAPI object in the payloadCreator method of createAsyncThunk, which I don't currently use. I'm trying to use dispatch as imported via hook in a component, not in my payloadCreator method.

Solution

  • This issue was ultimately caused by some sort of version mismatch between the react-redux and @reduxjs/toolkit packages. I removed my yarn.lock and deleted my node_modules, and reinstalled from scratch, and the issue disappeared!

    (Additionally, I also had to pin TypeScript at ~4.1.5 due to a separate issue installing TypeScript with Yarn 2.)