I am using redux and redux-thunk with typescript. I am trying to inject to a component through connect() a simple thunk action creator, using mapDispatchToProps.
Actions
export enum TestActionTypes {
  THUNK_ACTION = "THUNK_ACTION"
}
export interface ThunkAction {
  type: TestActionTypes.THUNK_ACTION;
}
export type TestAction = ThunkAction;
Action Creators
export function thunkActionCreator() {
  return function(dispatch: Dispatch<any>) {
    dispatch({ type: TestAction.THUNK_ACTION });
  };
Connected Component
interface DemoScreenState {}
interface OwnProps {}
interface StateProps {}
interface DispatchProps {
  testThunk: () => void;
}
type DemoScreenProps = StateProps & DispatchProps & OwnProps;
class DemoScreen extends React.Component<
  DemoScreenProps,
  DemoScreenState
> {
  constructor(props: DemoScreenProps) {
    super(props);
  }
  componentDidMount() {
    this.props.testThunk();
  }
  render() {
    return null;
  }
}
function mapStateToProps(state: any): StateProps {
  return {};
}
function mapDispatchToProps(dispatch: Dispatch<any>): DispatchProps {
  return {
    testThunk: () => dispatch(thunkActionCreator())
  };
}
export default connect<StateProps, DispatchProps, OwnProps>(
  mapStateToProps,
  mapDispatchToProps
)(DemoScreen);
Store
 import { createStore, applyMiddleware } from "redux";
 import rootReducer from "./RootReducer";
 import thunk from "redux-thunk";
 const store = createStore(rootReducer, applyMiddleware(thunk));
 export default store;
However, I am encountering two issue when using connect(). First of all, I get a type error for the declaration of testThunk in mapDispatchToProps.
Argument of type '(dispatch: Dispatch) => void' is not assignable to parameter of type 'Action'. Property 'type' is missing in type '(dispatch: Dispatch) => void'. I don't know how to handle this, as by definition a thunk is different from a plain action.
* EDIT 24/10/2018 * cf answer below, using:
"@types/react-redux": "^6.0.9",
"react": "16.3.1",
"react-native": "~0.55.2",
"react-redux": "^5.0.7",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0"
                1. dispatch: It is a method used to dispatch actions, that can be received by reducers. 2. getState: It gives access to store inside the thunk function.
But on the other hand, for bigger projects, Redux-Thunk may sometimes get you in trouble, as it can be hard to scale if your side effect or asynchronous logic increases, whereas in the case of Redux-Saga, it comes power-packed with some amazing things such as concurrent side effects, canceling side effects, debouncing ...
Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
I finally managed to get a properly typed connected component, mainly using this as a starting point https://github.com/reduxjs/redux-thunk/blob/master/test/typescript.ts for proper async action creator typing. The key is to properly type your store, and to use ThunkDispatch and ThunkAction from redux-thunk.
Store.tsx
import { createStore, applyMiddleware, combineReducers } from "redux";
import { playground, IState } from "./reducer";
import thunk, { ThunkMiddleware } from "redux-thunk";
import { IActions } from "./actions";
export interface RootState {
  playground: IState;
}
export type RootActions = IActions;
const rootReducer = combineReducers({ playground });
const store = createStore<RootState, RootActions, {}, {}>(
  rootReducer,
  applyMiddleware(thunk as ThunkMiddleware<RootState, RootActions>)
Actions.tsx
import { Action } from "redux";
import { ThunkAction } from "redux-thunk";
import { RootState, RootActions } from "./store";
type ThunkResult<R> = ThunkAction<R, RootState, undefined, RootActions>;
//Actions
export enum TestActionTypes {
  THUNK_ACTION = "THUNK_ACTION",
}
export interface AsyncAction extends Action {
  type: TestActionTypes.THUNK_ACTION;
}
export type IActions = AsyncAction;
//Action creator
export function testAsyncActionCreator(): ThunkResult<void> {
  return (dispatch, getState) => {
    console.log(getState);
    dispatch({ type: TestActionTypes.THUNK_ACTION });
  };
}
Reducer.tsx
import { IActions, TestActionTypes } from "./actions";
import { Reducer } from "redux";
export interface IState {}
const defaultValue: IState = {};
export const playground: Reducer<IState, IActions> = (
  state = defaultValue,
  action
) => {
  switch (action.type) {
    default:
      return state;
  }
};
ConnectedComponent.tsx
...
import { connect } from "react-redux";
import { RootState, RootActions } from "./redux/store";
import { syncActionCreator, testGetState } from "./redux/actions";
import { ThunkDispatch } from "redux-thunk";
interface OwnProps {}
interface StateProps {}
interface DispatchProps {
  test: () => void;
}
type Props = OwnProps & StateProps & DispatchProps;
class DumbContainer extends React.Component<Props> {
  render() {
    return (
      ...
    );
  }
}
const mapStateToProps = (state: RootState): StateProps => {
  return {
    text: state.playground.text
  };
};
const mapDispatchToProps = (
  dispatch: ThunkDispatch<RootState, undefined, RootActions>
): DispatchProps => {
  return {
    test: () => {
      dispatch(testAsyncActionCreator());
    }
  };
};
export default connect<StateProps, DispatchProps, OwnProps, RootState>(
  mapStateToProps,
  mapDispatchToProps
)(DumbContainer);
I also have a playground project here https://github.com/vbvxx/playground-reactnative-typescript
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With