web-dev-qa-db-fra.com

Taper fortement la connexion de react-redux avec dactylographié

Je reçois une erreur en essayant de taper les paramètres de mon composant React. Je voudrais taper uniquement les propriétés sur les accessoires et l'état d'un composant, mais lorsque je le fais en utilisant Redux, j'obtiens une erreur lorsque je passe mapStateToProps à la fonction de connexion.

Voici le code du composant:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import FileExplorer from '../components/file-Explorer/file-Explorer';
import { ISideMenu, ISideMenuState } from '../models/interfaces/side-menu';

    class SideMenu extends Component<ISideMenu, ISideMenuState> {
        render() {
            return (
                <div>
                    {this.props.fileExplorerInfo !== null &&
                        <FileExplorer fileExplorerDirectory={this.props.fileExplorerInfo.fileExplorerDirectory}/>
                    }
                </div>
            );
        }
    }

    const mapStateToProps = (state: ISideMenuState) => {
        return {
            fileExplorerInfo: state.fileExplorer
        };
    };

    export default connect<ISideMenu, null, ISideMenuState>(mapStateToProps)(SideMenu);

Donc, l'erreur se produit sur cette ligne:

export default connect<ISideMenu, null, ISideMenuState>(mapStateToProps)(SideMenu);

et lorsque je survole le mot "mapStateToProps" dans cette ligne, je vois l'erreur:

Argument of type '(state: ISideMenuState) => { fileExplorerInfo: FileDirectoryTree | null; }'
is not assignable to parameter of type 'MapStateToPropsParam<ISideMenu, ISideMenuState, {}>'.
  Type '(state: ISideMenuState) => { fileExplorerInfo: FileDirectoryTree | null; }' is not
assignable to type 'MapStateToProps<ISideMenu, ISideMenuState, {}>'.
    Types of parameters 'state' and 'state' are incompatible.
      Type '{}' is not
assignable to type 'ISideMenuState'.
        Property 'fileExplorer' is missing in type '{}'.

Et ce sont les deux interfaces que j'utilise dans le composant React:

export interface ISideMenu {
    fileExplorerInfo: FileExplorerReducerState | null;
}

export interface ISideMenuState {
    fileExplorer: FileDirectoryTree | null;
}

Tout aperçu de cette erreur sera grandement apprécié!

13
Shawn

Lorsque vous utilisez des génériques, vous obtenez le mauvais emplacement des interfaces:

Lorsque vous déclarez votre composant React:

class Comp extends Component<ICompProps, ICompState>

Avec ICompProps et ICompState sont respectivement les accessoires et l'état interne de votre composant.

Lorsque vous utilisez connect:

connect<IMapStateToProps, IMapDispatchToProps, ICompProps, IReduxState>

IMapStateToProps représente ce qui est retourné par votre fonction mapStateToProps(). IMapDispatchToProps représente ce qui est retourné par votre fonction mapDispatchToProps(). ICompProps représente vos React (comme ci-dessus) IReduxState représente l'état Redux de votre application

Donc dans votre exemple particulier:

Lors de la déclaration de votre composant React:

class SideMenu extends Component<ISideMenu, {}>

Utilisez ISideMenu pour les accessoires et {} (État vide) pour l'état car vous n'utilisez aucun état.

Lors de l'utilisation de connect:

connect<ISideMenu, {}, ISideMenu, ISideMenuState>(mapStateToProps)(SideMenu);

Vous pouvez utiliser ISideMenu à la fois comme vos accessoires de composant React et l'objet renvoyé par mapStateToProps. Mais en pratique, il peut être préférable de créer 2 interfaces distinctes.

Dans mes applications, je ne peux généralement pas être gêné de taper l'objet de retour mapStateToProps donc j'utilise simplement:

connect<{}, {}, ISideMenu, ISideMenuState>(mapStateToProps)(SideMenu);
22
klugjo

J'espère que cela ne vous dérange pas si je supprime certains antipatterns du code ci-dessus. Veuillez vérifier les commentaires que j'ai ajoutés. J'ai également ajouté withRouter pour mieux illustrer le modèle

import * as React from "react";
import { bindActionCreators } from "redux";
import { withRouter, RouteComponentProps } from "react-router";
import { connect } from "react-redux";
import { compose } from "recompose";

// OUR ROOT REDUX STORE STATE TYPE
import { State } from "../redux"; 

import FileExplorer from "../components/file-Explorer/file-Explorer";

// interfaces starting with 'I' is an antipattern and really
// rarely needed to be in a separate file

// OwnProps - that's what external code knows about out container
type OwnProps = {};

// this comes from redux
type StateProps = {
  fileExplorer: FileDirectoryTree | null;
};

// resulting props - that what container expects to have
type Props = OwnProps & StateProps & RouteComponentProps<any>;

// no need to have a class, SFC will do the same job
const SideMenu: React.SFC<Props> = props => {
  return (
    <div>
      {this.props.fileExplorerInfo !== null && (
        <FileExplorer
          fileExplorerDirectory={
            this.props.fileExplorerInfo.fileExplorerDirectory
          }
        />
      )}
    </div>
  );
};

// compose (from recompose lib) because usually we have more than 1 hoc
// say let's add withRouter just for fun



export default compose<Props, OwnProps>(

  withRouter,

  // it's important to read the typings:
  // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-redux/index.d.ts
  connect<StateProps, {}, {}, State>(s => ({
    fileExplorerInfo: s.fileExplorer
  })),

)(SideMenu);
4
Daniel Khoroshko