web-dev-qa-db-fra.com

Comment utiliser TypeScript avec withRouter, connect, React.Component et des propriétés personnalisées?

J'ai un React Component qui utilise connect, withRouter et reçoit des propriétés personnalisées. J'essaie de le convertir en TypeScript et je ' Je me demande si je fais ça correctement. Au moins, je n'ai plus d'erreurs, maintenant.

C'est le code qui montre le concept:

import * as React from 'react'
import { connect } from 'react-redux';
import { withRouter, RouteComponentProps } from 'react-router';

import { 
  fetchTestLists,
  newTestList,
  displayTestList,
} from '../../../actions/index';

interface StateProps {
  testList: any;    // todo: use the type of state.myList to have validation on it
}

interface DispatchProps {
  fetchTestLists: () => void;
  newTestList: () => void;
  displayTestList: (any) => void;   // todo: replace any with the actual type
}

interface Props {      // custom properties passed to component
  className: string;
}

type PropsType = StateProps & DispatchProps & Props;


class MyTest extends React.Component<PropsType & RouteComponentProps<{}>, {}> {
  constructor(props) {
    super(props);
    this.handleCellClick = this.handleCellClick.bind(this);
    this.newTestList = this.newTestList.bind(this);
  }

  componentDidMount() {
    this.props.fetchTestLists();
  }

  handleCellClick(row, column, event) {
    this.props.displayTestList(row);
  }

  newTestList(e) {
    this.props.newTestList()
  }

  render() {
    return (
      <div className={this.props.className}>
      </div>
    );
  }
}

const mapStateToProps = (state): StateProps => ({
  testList: state.myList,   // todo: define a type for the root state to have validation here
});

const dispatchToProps = {
  fetchTestLists,
  newTestList,
  displayTestList,
};

export default withRouter<Props & RouteComponentProps<{}>>(connect<StateProps, DispatchProps>(
  mapStateToProps,
  dispatchToProps,
)(MyTest) as any);

Le composant est utilisé comme ceci: <MyTest className={"active"} />

J'ai dû beaucoup expérimenter pour que cela fonctionne. Par exemple:

1) Lorsque je laisse de côté les types de withRouter comme ceci: export default withRouter(connect... Ensuite, je reçois TS2339: Property 'className' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<RouteComponentProps<any>, never>, C...'. Ceci a été suggéré en quelque sorte ici: React router in TypeScript- both router and own props , bien que je ne comprenne pas ce concept.

2) Si vous vous interrogez sur la dernière ligne as any, cela est lié à https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18999 et j'obtiens cette erreur sans:

 TS2345: Argument of type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' is not assignable to parameter of type 'ComponentType<Props & RouteComponentProps<{}>>'.
 Type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' is not assignable to type 'StatelessComponent<Props & RouteComponentProps<{}>>'.
 Type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' provides no match for the signature '(props: Props & RouteComponentProps<{}> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.

Est-ce donc la bonne façon de procéder? Où voyez-vous des problèmes? J'utilise essentiellement toutes les dernières versions, voici un extrait de mon package.json:

"react": "^16.2.0",
"redux": "^3.7.2",
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^4.0.8",
...
"TypeScript": "^2.7.2",
"@types/react-redux": "^5.0.15",
"@types/react-router": "^4.0.22",
"@types/react-router-dom": "^4.2.4",
"@types/react": "^16.0.38",
"@types/react-dom": "^16.0.4",
16
chmielot

J'essaie de réécrire votre exemple et je me retrouve avec ce code:

import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router';

import { 
  fetchTestLists,
  newTestList,
  displayTestList,
} from '../../../actions/index';
import { Dispatch, bindActionCreators, AnyAction } from 'redux';

interface IStateProps {
  testList: IListType;    // todo: use the type of state.myList to have validation on it
}

interface IDispatchProps {
  fetchTestLists: () => AnyAction;
  newTestList: () => AnyAction;
  displayTestList: (value: string) => AnyAction;   // todo: replace any with the actual type
}

interface IProps {      // custom properties passed to component
  className: string;
}

type PropsType = IStateProps & IDispatchProps & IProps;

class MyTestComponent extends React.Component<PropsType & RouteComponentProps<{}>, {}> {
  constructor(props: PropsType & RouteComponentProps<{}>) {
    super(props);
    this.handleCellClick = this.handleCellClick.bind(this);
    this.newTestList = this.newTestList.bind(this);
  }

  public componentDidMount() {
    this.props.fetchTestLists();
  }

  public handleCellClick(row, column, event) {
    this.props.displayTestList(row);
  }

  public newTestList(e) {
    this.props.newTestList();
  }

  public render(): JSX.Element {
    return (
      <div className={this.props.className}>
      </div>
    );
  }
}

export const MyTest = connect(
  (state: IAppState, ownProps: IProps) => ({
      testList: state.testList,
      ...ownProps,
  }),
  (dispatch: Dispatch) => bindActionCreators<AnyAction, Pick<IDispatchProps, keyof IDispatchProps>>(
    { displayTestList, fetchTestLists, newTestList },
    dispatch,
),
)(withRouter(MyTestComponent));

interface IListType {
  someProp: string;
}

interface IAppState {
  testList: IListType;
  differentList: IListType;
}

J'ai changé export par défaut pour assigner le résultat de la classe MyTestComponent avec connect et withRouter HOC dans MyTest. Ensuite, j'importe MyTest composant comme celui-ci

import { MyTest } from './MyTest' 

J'ai ajouté des interfaces pour décrire toutes les propriétés qui ont été passées du composant parent, utilisez également withRouter et connect de manière différente (plus lisible pour moi).

J'espère que ce sera utile

1
kwdowik