Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript with Redux, how to use connect without re-declaring action definition?

I'm having some inconveniences with typescript and redux-thunk actions.

Basically my components make heavy use of react-redux connect to bind action creators, the problem is, when I create the interface for this redux actions within the component, I have to re-declare the function definition because it gets lost on the connect call.

Here is a sample code:

Action Creator

import api from './api';
import { Dispatch } from 'redux';
import { IReduxAction } from 'model';

export const FETCH_ENTITY = 'location/FETCH_ENTITY';
export function fetchEntity(id: string) {
  return (dispatch: Dispatch<IReduxAction>) => {
    dispatch({type: `${FETCH_ENTITY}_REQUEST`, payload: {id}});

    return api.fetchEntity(id)
              .then((res) => {

                dispatch({ type: `${FETCH_ENTITY}_DONE`, payload: res });
                return res;
              })
              .catch((err) => {
                dispatch({type: `${FETCH_ENTITY}_FAIL`, payload: err, error: true});
                throw err;
              });
  };
}

Component

import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import {
  fetchEntity,
} from 'actions';

interface IStateProps {
  startingEntities: any[];
}

interface IDispatchProps {
  fetchEntity: (id: string) => Promise<any>; // <- this is what I'm trying to get rid of, basically whenever I change the action creator I have to change this definition everytime
}

class Dashboard extends React.Component<IStateProps & IDispatchProps, void> {

  public render() {

    return (<div>...RENDER SOMETHING...</div>);
  }

}

const mapStateToProps = (state: any, ownProps: any) => {
  const startingEntities = foo(); // not important

  return {
    startingEntities,
  };
};

const mapDispatchToProps = (dispatch: any) => {
  return bindActionCreators({
    fetchEntity
  }, dispatch);
};

export default connect<IStateProps, IDispatchProps, void>(mapStateToProps, mapDispatchToProps)(Dashboard);

Is there anyway to declare this binded action creator without having to re-declare it everytime on each file?

like image 713
Oscar Franco Avatar asked Jun 23 '17 09:06

Oscar Franco


2 Answers

You don't need to define the function type again. You can use typeof:

interface IDispatchProps {
  fetchEntity: typeof fetchEntity
}
like image 81
Ninten Avatar answered Nov 08 '22 10:11

Ninten


I'm not very familiar with TypeScript (mostly have just done index.d.ts files for library interfaces) but you should be able to get the type system to do the work for you by correctly typing fetchEntity and exporting it for IDispatchProps to use. Something like

//...

import { ThunkAction } from 'redux-thunk'

//...

export interface FetchEntity {
  (id: string): ThunkAction<Promise<any>, IReduxAction, any>;
}

//...

export const fetchEntity: FetchEntity = (id: string) => {
  return (dispatch: Dispatch<IReduxAction>) => {
    //...
  };
}

And then you can import it and use the same definition

//...

import {
  fetchEntity,
  FetchEntity
} from 'actions';

//...

interface IDispatchProps {
  fetchEntity: FetchEntity
}

//...
like image 22
Michael Peyper Avatar answered Nov 08 '22 10:11

Michael Peyper