-
-
Save milankorsos/ffb9d32755db0304545f92b11f0e4beb to your computer and use it in GitHub Desktop.
import {Action, ActionCreator, Dispatch} from 'redux'; | |
import {ThunkAction} from 'redux-thunk'; | |
// Redux action | |
const reduxAction: ActionCreator<Action> = (text: string) => { | |
return { | |
type: SET_TEXT, | |
text | |
}; | |
}; | |
// Redux-Thunk action | |
const thunkAction: ActionCreator<ThunkAction<Action, IState, void>> = ( | |
text: string | |
) => { | |
return (dispatch: Dispatch<IState>): Action => { | |
return dispatch({ | |
type: SET_TEXT, | |
text | |
}); | |
}; | |
}; | |
// Async Redux-Thunk action | |
const asyncThinkAction: ActionCreator< | |
ThunkAction<Promise<Action>, IState, void> | |
> = () => { | |
return async (dispatch: Dispatch<IState>): Promise<Action> => { | |
try { | |
const text = await Api.call(); | |
return dispatch({ | |
type: SET_TEXT, | |
text | |
}); | |
} catch (e) {} | |
}; | |
}; |
Hey, @tswaters!
I'm also trying to tie-up Typescript with React/Redux/Thunk/etc 😃
For those who are just like me, one should mention that at first, you need to import all the stuff:
import { ThunkAction, ThunkDispatch } from 'redux-thunk';
Thank you! 👍
these types might be useful to people using react 4 & redux thunk 2.3.
for typing thunks
ActionCreator<ThunkAction<Promise<any>,IState,null,AnyAction>>
where Promise<any>
is an example of return type of the thunk
and AnyAction
is the type of action you'll dispatch
and for typing dispatch
ThunkDispatch<IState, null, AnyAction>
similar arguments, see source of redux-thunk
This works when I pass mapDispatchToProps
as a function.
impression IProps {
thunkAction: (arg: IArg) => Promise<IArg>
}
class Comp extends React.Component<IProp> { ... }
const mapStateToProps = ...
const mapDispatchToProps = (dispatch: ThunkDispatch<IState, any, IAction>) => ({
thunkAction: (arg: Partial<IArg>) =>
dispatch(thunkAction(arg))
});
connect(mapStateToProps, mapDispatchToProps)(Comp);
Doesn't work when I pass it as an object
// Type 'ThunkAction<Promise<ThunkAction>, IState, any, IAction<any>>' is not assignable to type 'Promise<IArg>'.
connect(mapStateToProps, { thunkAction })(Comp);
Whats missing here?
As @aaronlifton2 mentioned up above it looks like the type signature for ThunkAction has changed in newer versions of redux-thunk
.
export type ThunkAction<R, S, E, A extends Action> = (
dispatch: ThunkDispatch<S, E, A>,
getState: () => S,
extraArgument: E
) => R;
Here's an example of how I set up my Thunk types: https://gist.github.com/seansean11/196c436988c1fdf4b22cde308c492fe5
This works when I pass
mapDispatchToProps
as a function.impression IProps { thunkAction: (arg: IArg) => Promise<IArg> } class Comp extends React.Component<IProp> { ... } const mapStateToProps = ... const mapDispatchToProps = (dispatch: ThunkDispatch<IState, any, IAction>) => ({ thunkAction: (arg: Partial<IArg>) => dispatch(thunkAction(arg)) }); connect(mapStateToProps, mapDispatchToProps)(Comp);
Doesn't work when I pass it as an object
// Type 'ThunkAction<Promise<ThunkAction>, IState, any, IAction<any>>' is not assignable to type 'Promise<IArg>'. connect(mapStateToProps, { thunkAction })(Comp);
Whats missing here?
Has this been solved yet? I get a similar error when trying to use the object form of mapDispatch; using the function form works fine.
Unless I'm missing something obvious, this is a rather sharp edge :(
Following the introductory Redux docs, you are guided to prefer the object form of mapDispatch, and then in the typescript section, you are introduced the ThunkDispatch
type, which doesn't work with the object form of mapDispatch.
I am migrating to Typescript for fun, but I am not understanding how I can avoid all the boilerplate when I am writing my actions and reducers like this:
localUserReducer.js
const initialState = {
fetching: false,
fetched: false,
user: undefined,
errors: undefined,
};
export default function (state = initialState, action) {
switch (action.type) {
case 'GET_USER_PENDING':
return {
...state,
fetching: true,
};
case 'GET_USER_FULFILLED':
return {
...state,
fetching: false,
fetched: true,
user: action.payload,
};
case 'GET_USER_REJECTED':
return {
...state,
fetching: false,
errors: action.payload,
};
default:
return state;
}
}
localUserActions.js
import axios from 'axios';
export const getUser = () => async (dispatch) => {
dispatch({ type: 'GET_USER_PENDING' });
try {
const { data } = await axios.get('/api/auth/local/current');
dispatch({ type: 'GET_USER_FULFILLED', payload: data });
} catch (err) {
dispatch({ type: 'GET_USER_REJECTED', payload: err.response.data });
}
};
Would I be expected to write an interface for each _PENDING
, _FULFILLED
, and _REJECTED
action?
I may have a huge misunderstand of redux-thunk as I am a newbie. I don't understand how I can send _REJECTED
actions if I use the implementation of Typescript and redux-thunk documented here: https://redux.js.org/recipes/usage-with-typescript#usage-with-redux-thunk
export type ThunkResult<R> = ThunkAction<R, IStoreState, null, IStoreActions>
Just want to say thank you, I struggled with it and your answer helped me solve the problem. 😄
Thanks @milankorsos!
Please hellp me )))
export type ThunkResult2<R> = ThunkAction<R, AppStateType, null, Action>
export type ThunkDispatch2 = ThunkDispatch<AppStateType, null, Action>
export const login = (auth: any): ThunkResult2<Promise<{a: boolean, b: string}>> => async (dispatch: ThunkDispatch2) => {
const res = await AuthApi.authentication(auth);
const r = dispatch(AuthActions.login(new Auth({ ...res })));
return {
a: true,
b: '123'
} // this very well work
}
// but
......
onSubmit={ async (values) => {
const res = await dispatch(login(values))
console.log(res.a) // this problem
}}
TS2339: Property 'a' does not exist on type 'ThunkAction { a: boolean; b: string; }>, CombinedState{ auth: never; }>, null, Action >'
I still can't locate IStoreState
Maybe this is right place to use "any" when i must make type for thunk action in my props.
The response from timdorr pointed at the tests, which are quite helpful actually. From those tests, I picked up a pattern that works pretty well. In my main reducers file (where I pull together types for all of the actions and state slices), I'll export a few types for use elsewhere in the application:
When defining an async thunk action, they'll use
ThunkResult<R>
as the return type, this looks like this:This also works with async functions (and by nature,
promise.then
works):When using
mapDispatchToProps
, I'll useThunkDispatch
.... this only seems necessary when gettingdispatch
throughreact-redux
-- if I have a reference to the store, the dispatch action seems to come with the additional typings for handling dispatches with thunks fine.... pulling it all together for a connected container --