Comments (46)
FWIW, I believe the reason this is coming up as an issue with react-redux v8 is because the v7 useDispatch()
was typed as:
// NOTE: the first overload below and note above can be removed if redux-thunk typings add an overload for
// the Dispatch function (see also this PR: https://github.com/reduxjs/redux-thunk/pull/247)
export function useDispatch<TDispatch = Dispatch<any>>(): TDispatch;
export function useDispatch<A extends Action = AnyAction>(): Dispatch<A>;
The first function overload means you can dispatch literally anything (as a workaround for redux-thunk
).
But in v8 it's now typed as:
export declare const useDispatch: <AppDispatch extends Dispatch<AnyAction> = Dispatch<AnyAction>>() => AppDispatch;
which only allows for Action
objects.
So this issue isn't "new" it's just been masked by the loose types in the old react-redux
type definitions.
That's just background for why this is coming up now. If you're hitting this error now as a user you should either:
- Use Redux Toolkit and define typed hooks (which will be correctly typed and allow thunk actions)
- If you're using vanilla Redux, most bundlers won't like
import 'redux-thunk/extend-redux'
in a regular TypeScript file because they think you're trying to import JavaScript so you can either:- Use
import type {} from 'redux-thunk/extend-redux'
- Put
/// <reference types="redux-thunk/extend-redux" />
in a.d.ts
file that's included in your type-checking - Put
import 'redux-thunk/extend-redux'
in a.d.ts
file that's included in your type-checking
- Use
from redux-thunk.
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { AnyAction, combineReducers } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
const rootReducers = combineReducers();
type AppState = ReturnType<typeof rootReducers>;
type TypedDispatch<T> = ThunkDispatch<T, any, AnyAction>;
export const useAppDispatch = () => useDispatch<TypedDispatch<AppState>>();
export const useAppSelector: TypedUseSelectorHook<AppState> = useSelector;
// USE
const dispatch = useAppDispatch();
const state = useAppSelector((state: AppState) => state.xxx);
from redux-thunk.
@Methuselah96's solution pointed me in the right direction but since redux-thunk/src/types.ts
currently has a typescript error which caused my pipelines to fail, I created a redux-thunk.d.ts file:
// This is required to fix redux thunk errors introduced with react-redux version 8
import 'redux'
declare module 'redux' {
/**
* Overload for bindActionCreators redux function, returns expects responses
* from thunk actions
*/
function bindActionCreators<
ActionCreators extends ActionCreatorsMapObject<any>
>(
actionCreators: ActionCreators,
dispatch: Dispatch
): {
[ActionCreatorName in keyof ActionCreators]: ReturnType<
ActionCreators[ActionCreatorName]
> extends ThunkAction<any, any, any, any>
? (
...args: Parameters<ActionCreators[ActionCreatorName]>
) => ReturnType<ReturnType<ActionCreators[ActionCreatorName]>>
: ActionCreators[ActionCreatorName]
}
/*
* Overload to add thunk support to Redux's dispatch() function.
* Useful for react-redux or any other library which could use this type.
*/
export interface Dispatch<A extends Action = AnyAction> {
<ReturnType = any, State = any, ExtraThunkArg = any>(
thunkAction: ThunkAction<ReturnType, State, ExtraThunkArg, A>
): ReturnType
}
}
from redux-thunk.
@Methuselah96's solution pointed me in the right direction but since
redux-thunk/src/types.ts
currently has a typescript error which caused my pipelines to fail, I created a redux-thunk.d.ts file:// This is required to fix redux thunk errors introduced with react-redux version 8 import 'redux' declare module 'redux' { /** * Overload for bindActionCreators redux function, returns expects responses * from thunk actions */ function bindActionCreators< ActionCreators extends ActionCreatorsMapObject<any> >( actionCreators: ActionCreators, dispatch: Dispatch ): { [ActionCreatorName in keyof ActionCreators]: ReturnType< ActionCreators[ActionCreatorName] > extends ThunkAction<any, any, any, any> ? ( ...args: Parameters<ActionCreators[ActionCreatorName]> ) => ReturnType<ReturnType<ActionCreators[ActionCreatorName]>> : ActionCreators[ActionCreatorName] } /* * Overload to add thunk support to Redux's dispatch() function. * Useful for react-redux or any other library which could use this type. */ export interface Dispatch<A extends Action = AnyAction> { <ReturnType = any, State = any, ExtraThunkArg = any>( thunkAction: ThunkAction<ReturnType, State, ExtraThunkArg, A> ): ReturnType } }
it worked just fine! thank you very much!
from redux-thunk.
@sevgeek : I think the issue here is the use of .prepend([SomeMiddleware])
, and more specifically, the use of an array as the argument.
If I uncomment that API slice reducer, and then remove the square brackets from both the .prepend
and .concat
lines, the AppDispatch
type looks to be correct.
We do some complex types manipulation to figure out how middleware might alter the type of store.dispatch
, and it looks like that just doesn't work right if you pass in an array as an argument. (In the case where you wanted to add multiple middleware at once, you pass them in as separate args, like .concat(middleware1, middleware2)
.)
from redux-thunk.
@markerikson I transferred the pluggable middlewares from .prepend([])
to .concat(middleware1, middleware2, ...)
or .concat([middleware1, middleware2, ...])
and the type error disappeared.
Thank you very much for your help! 👍🏻
from redux-thunk.
For me import 'redux-thunk/extend-redux';
was causing a Webpack error:
ERROR in ./source/scripts/app.tsx 5:0-34
Module not found: Error: Can't resolve 'redux-thunk/extend-redux' in 'C:\Users\nbier\Documents\test-app'
I ended up using an empty type-import to clue Webpack into the fact I wasn't trying to import a JS module:
import type {} from 'redux-thunk/extend-redux';
from redux-thunk.
@mendesbarreto unfortunately I don't know what the ThunkAction
variable looks like in this example, or how the middleware was set up, or what TS version you're using, or how TS itself is configured. So, I really need to see an actual project if at all possible, so I can investigate it myself.
from redux-thunk.
How are you dispatching it? If you're using useDispatch
make sure you're using a typed hook.
from redux-thunk.
@pailhead I'm not asking about "why" you're trying to do this - I'm telling you how to fix it :)
from redux-thunk.
Do you actually have the store configured to use the thunk middleware?
It would really help to see a CodeSandbox or a repo that shows this problem happening.
from redux-thunk.
I will create a project. But is really simple to reproduce, you just install the [email protected]
in a typescript project and try to use store.dispatch(ThunkAction);
from a redux store.
from redux-thunk.
@mendesbarreto
Are you by any chance using react-redux v8?
I have created the following Issue.
maybe related
#247
from redux-thunk.
Got the same issue, only for react-redux v8. Would love to have a fix for this.
from redux-thunk.
react-redux v8 was where I was running into the issue as well. Are you importing redux-thunk/extend-redux
at all? Did you try import type
mentioned in my previous comment? What's the exact error message you're getting?
from redux-thunk.
I'll repeat my usual refrain :)
REPROS! WE NEED REPROS!
We really need actual projects that show the error happening so I can investigate.
from redux-thunk.
Huh, good eye! I suppose I accidentally "fixed" this while doing some cleanup on the types :)
from redux-thunk.
@markerikson Here's your repro. :)
The line I linked to errors with Webpack because it's trying to import 'redux-thunk/extend-redux'
as a JS module.
from redux-thunk.
Heh, thanks :)
So backing up, it sounds like there's two different "issues" going on here:
ThunkAction
not dispatching correctly, which we think is due to a change in the v8useDispatch
types when the user wasn't actually following our TS setup guidelines- The separate question of "what is the right way to import the extended global types". Given that it's a pure types-only file, I think it's safe to assume that it has to be done as
import type
and I just flat-out was wrong with my import instructions.
Is there anything else going on atm beyond those? Seems like we could consider this resolved if not.
from redux-thunk.
@markerikson Thanks for looking into this. I am at work but I'll see if I can reproduce the bug locally later. The bug occurred to me in a project where I don't use the toolkit but the old redux action/reducer pattern instead.
from redux-thunk.
@Methuselah96 I was just importing directly import { ThunkAction } from 'redux-thunk'
. I'll test out the other way to import later as I have recently reverted the react-redux version back to v7.
from redux-thunk.
@allicanseenow Yeah, an import from 'redux-thunk'
won't extend the Redux types to allow dispatching thunk actions, you have to import 'redux-thunk/extend-redux'
somewhere.
from redux-thunk.
@markerikson Yeah, that sounds right.
Although on your first point, for vanilla Redux users the TypeScript guide does not mention the need to import redux-thunk/extend-redux
. Without that import the most obvious way of typing a useDispatch
hook with Dispatch<ThunkAction>
will not work. But Redux Toolkit should work fine.
from redux-thunk.
In theory, our advice of type AppDispatch = typeof store.dispatch
ought to work even with vanilla createStore
+ applyMiddleware
, but it's been so long since I've tried it that I'm not 100% sure.
from redux-thunk.
Yeah if the middleware is the only enhancer it might work, but I don't think composing multiple enhancers with the Redux types works right now without reduxjs/redux#3776 (can we get that merged :)). I haven't tested it out either, so maybe it would work.
from redux-thunk.
Can confirm that type AppDispatch = typeof store.dispatch
does not work with vanilla createStore
+ applyMiddlware
. Here's the CodeSandbox (not a working app, just checking the types). I didn't get an error in the TypeScript playground, but that might be because we haven't removed @types/react-redux
. I don't think the PR mentioned above would fix this scenario, but I haven't dug into why this doesn't work.
from redux-thunk.
Huh, interesting. We do have a bunch of custom type-level code in configureStore
to ensure that dispatch
ends up with the correct types based on the supplied middleware. I would have thought that applyMiddleware
would at least do some of that behavior.
from redux-thunk.
It should, but I don't think people rely on createStore
doing that in practice since it's been at least partially broken for a long time.
from redux-thunk.
I'm using Redux Toolkit and upgrading the package version to at least 1.7.0 solved this issue for me.
"@reduxjs/toolkit": "1.7.2",
from redux-thunk.
Today I ran into the same problem when I wanted to dispatch
initiate action for my API endpoint from redux-toolkit/rtk-query
library.
@reduxjs/toolkit: 1.8.2
react-redux: 8.0.2
from redux-thunk.
How are you dispatching it? If you're using
useDispatch
make sure you're using a typed hook.
@Methuselah96 Yes, I am using typed useAppDispatch
hook in custom hook:
export type { RootStore, AppDispatch };
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootStore> = useSelector;
export default store;
function useDispatchSafeDealAction() {
const dispatch = useAppDispatch();
const dispatchAction = React.useCallback(() => {
dispatch(OAuthAPI.endpoints.getOAuthToken.initiate());
}, [dispatch]);
return [dispatchSafeDealAction];
}
Typescript error:
from redux-thunk.
@sevgeek : can you show your actual store setup code, preferably as a CodeSandbox or Github repo?
from redux-thunk.
@sevgeek : can you show your actual store setup code, preferably as a CodeSandbox or Github repo?
@markerikson Yes, sure. I use for configure store very simple reducer, my API slice reducer and additional listener middleware.
https://codesandbox.io/s/react-redux-toolkit-wbzkgc?file=/src/store/index.ts
I think I found a script to reproduce the error.
When I disable the connection of my [APISlice.reducerPath]
, type AppDispatch
gets the correct types and allows me to work with ThunkDispatch
.
When I return connection my [APISlice.reducerPath]
, AppDispatch
get a different type
and then I get type error in my custom hook: https://codesandbox.io/s/react-redux-toolkit-wbzkgc?file=/src/hooks/index.tsx
from redux-thunk.
Forgot to comment. I was able to resolve the issue globally in my personal project by importing 'redux-thunk/extend-redux', as suggested by @Methuselah96
I created a custom file custom.d.ts
that has
import 'redux-thunk/extend-redux'
And I include it in tsconfig.json
:
{
...,
"include" : ["custom.d.ts"],
}
from redux-thunk.
Many Thanks, this workaround works for me too: import 'redux-thunk/extend-redux'
from redux-thunk.
Hi, I could do store.dispatch(ThunkAction)
by type casting thunkMiddleWare
before passing to applyMiddleware
like applyMiddleware(thunkMiddleWare as ThunkMiddleware<State, Actions>)
. (in case you created store with deprecated createStore
util).
See: https://codesandbox.io/s/redux-thunk-ts-forked-lzl9d2?file=/src/index.ts
The type of store is:
// without type casting
const store: Store<State, Action<any>> & {
dispatch: unknown;
}
store.dispatch(testGetState()); // complained like `Argument of type 'ThunkResult<void>' is not assignable to parameter of type 'Action<any>'.ts(2345)`... :(
// with type casting
const store: Store<State, Action<any>> & {
dispatch: ThunkDispatch<State, undefined, Actions>;
}
store.dispatch(testGetState()); // working! :)
from redux-thunk.
@bokusunny : you really shouldn't ever use the TS Store
type, and definitely not to cast the store setup.
The right answer here is to use Redux Toolkit's configureStore
, which A) is the recommended way to create a Redux store in the first place, and B) has much better type inference than createStore
does.
from redux-thunk.
How can i make my own magical dispatch that injects stuff:
export const useAppDispatch = (): AppDispatch => {
const { modulePath } = useModuleContext()
const dispatch = useDispatch()
const moduleDispatch = useMemo(() => {
const stableMeta = { modulePath }
return (action: Parameters<typeof dispatch>[0]) => {
action.meta = stableMeta
dispatch(action)
}
}, [dispatch, modulePath])
return moduleDispatch
}
It's complaining it can't assign ThunkAction<...>
to AnyAction
.
from redux-thunk.
@pailhead : a few different issues with that:
- You should follow our TS guidelines for inferring the type of
dispatch
from the store setup, and then define pre-typed hooks that have that type baked in: https://redux.js.org/tutorials/typescript-quick-start#define-root-state-and-dispatch-types - You should use that type here:
const dispatch = useAppDispatch()
: - Instead of
(action: Parameters<typeof dispatch>[0])
, you should type that callback function with the type ofAppDispatch
instead - You should make sure to include
return
, likereturn dispatch(action)
from redux-thunk.
I don't want to use prepare
which seems to be the only way to type the meta
, and i want to use MyAction<T=void> = PayloadAction<T> & {meta: MyMeta}
, hence the idea to make this kind of a dispatch to complement that :(
from redux-thunk.
Type 'ThunkAction<...> is not assignable to type 'AnyAction
:(
from redux-thunk.
@pailhead I know. Please follow the instructions I gave you to fix that TS error!
from redux-thunk.
I'm going around in circles typing as AppDispatch
seems to leave my action as any
. typeof dispatch
is the same as AppDispatch
so other than returning the last dispatch i don't understand what your instructions are saying that i haven't already done. (Other than the thing where i make action:any
). Thanks anyway ❤️
export const useAppDispatch = (): AppDispatch => {
const { modulePath } = useModuleContext()
const dispatch : AppDispatch = useDispatch()
const moduleDispatch = useMemo(() => {
const stableMeta = { modulePath }
const foo:: AppDispatch = (action: any) => {
action.meta = stableMeta
return dispatch(action)
}
}, [dispatch, modulePath])
return moduleDispatch
}
Ie. this works, but action:any
.
from redux-thunk.
Assuming you're using Redux Toolkit and following our store setup that I linked,, AppDispatch
should roughly be something along the lines of ThunkDispatch & Dispatch<AnyAction>
(going from memory).
Then, you need to use that type for this new "override dispatch" function you're returning:
const moduleDispatch = useMemo(() => {
const stableMeta = { modulePath }
const mySpecialDispatch: AppDispatch = (action) => {
action.meta = stableMeta
return dispatch(action)
}
}, [dispatch, modulePath])
and definitely do not include action: any
!
So:
- Make sure that you have your store setup correct and that hovering over the inferred
AppDispatch
type shows that it includesThunkDispatch
. If this type is wrong, then nothing else will be typed right. - Use that type as shown above.
from redux-thunk.
(alias) type WarpDispatch = ThunkDispatch<{
layout: LayoutState;
}, undefined, AnyAction> & Dispatch<AnyAction>
Hm won't let me upload a screenshot, but basically this doesn't type action
in const mySpecialDispatch:AppDispatch = (action)...
(i get any
)
from redux-thunk.
@pailhead : well, it should :)
Honestly, using this issue thread as a chat support system doesn't work well.
If you're still having issues, please come by the #redux
channel in the Reactiflux Discord ( https://www.reactiflux.com ), and have a CodeSandbox or a Github repo that shows this happening.
The types and approach I told you will work, but it's also possible that there's something odd about your specific project setup and configuration that we don't know about.
from redux-thunk.
Related Issues (20)
- Why `redux` module extension is not part of index.d.ts file in package? HOT 1
- Passing `dispatch` instead of calling it.
- Empty payload in PENDING state HOT 1
- Why not next? HOT 1
- types wrong by infer in TS HOT 1
- No type hints when using dispatch HOT 4
- import 'redux-thunk/extend-redux' doesn't work HOT 12
- When running server getting expected errors HOT 2
- Current implemention doesn't allow Jest or any other mock tool to spy on dispatch or getState HOT 2
- TS do not show the error if payload is Partial<> HOT 1
- Can I abort redux thunk dispatches? Without Redux toolkit or aborting the fetch request itself. HOT 1
- Allow wrapped thunk actions for extensibility HOT 2
- When type: "module" is used in root package.json, redux-thunk cannot be applied without crashing HOT 8
- Warning in React-thunk while installing HOT 1
- Query About Redux Thunk HOT 1
- `thunk` as a named export? HOT 3
- redux thunk cannot be use as middleware using Typescript 5.0 or above HOT 1
- during integrating thunk into react-redux project, getting middleware is not a function error. HOT 1
- No default export for redux-thunk HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from redux-thunk.