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 customizedAppDispatch
type from the store that includes the thunk middleware types, and use that withuseDispatch
. Adding a pre-typeduseDispatch
hook keeps you from forgetting to importAppDispatch
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 AsyncThunkAction
s?
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.
A few things about my environment could also theoretically contribute to this issue, so I'm including them here for correctness:
ReactDOM
. See this page for an example on how this works.
<Provider>
component, instead of one <Provider>
at the root, but all share the same store.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.useAppDispatch(...)
. I'm using the correct definition in my code.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.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.)