强烈键入 react-redux 与 typescript 连接

Posted

技术标签:

【中文标题】强烈键入 react-redux 与 typescript 连接【英文标题】:Strongly typing the react-redux connect with typescript 【发布时间】:2018-06-25 20:06:54 【问题描述】:

我在尝试为我的反应组件键入参数时遇到错误。我想在组件的 props 和 state 上输入哪些属性,但是当我使用 Redux 这样做时,当我将 mapStateToProps 传递给 connect 函数时出现错误。

这是组件代码:

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);

所以错误发生在这一行:

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

当我将鼠标悬停在该行中的“mapStateToProps”一词上时,我看到了错误:

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 ''.

这是我在 react 组件中使用的两个接口:

export interface ISideMenu 
    fileExplorerInfo: FileExplorerReducerState | null;


export interface ISideMenuState 
    fileExplorer: FileDirectoryTree | null;

任何对此错误的见解将不胜感激!

【问题讨论】:

这里的 (state: ISideMenuState) 应该是你的 redux 存储状态类型,而不是组件状态 其实你的代码有问题 @Topsy 有 10 个不同的签名。github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/… 【参考方案1】:

当使用泛型时,接口的位置是错误的:

当你声明你的 React 组件时:

class Comp extends Component<ICompProps, ICompState>

ICompPropsICompState 分别是组件的 props 和内部状态。

当您使用连接时:

connect<IMapStateToProps, IMapDispatchToProps, ICompProps, IReduxState>

IMapStateToProps 表示您的 mapStateToProps() 函数返回的内容。 IMapDispatchToProps 表示您的 mapDispatchToProps() 函数返回的内容。 ICompProps 代表你的 React 组件道具(同上) IReduxState 代表你的 App 的 Redux 状态

所以在你的具体例子中:

在声明你的 React 组件时:

class SideMenu extends Component<ISideMenu, >

使用ISideMenu 作为道具,使用(空状态)作为状态,因为您不使用任何状态。

使用连接时:

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

你可以使用ISideMenu 作为你的 React 组件 props 和 mapStateToProps 返回的对象。但在实践中,最好创建 2 个单独的接口。

在我的应用程序中,我通常不会打扰输入 mapStateToProps 返回对象,所以我只需使用:

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

【讨论】:

但是,直到第 4 个 connect 泛型出现在游戏中——redux State 的荣耀中,它才能被称为强类型 同意,已编辑。只是根据 OP 迄今为止的发现提供增量帮助 或者直接使用通用...export default compose(connect&lt;, , any, any&gt;(mapStateToProps))(MyComponent) 当我使用这个模式时,它给了我以下错误,Untyped function calls may not accept type arguments【参考方案2】:

如果我从上面的代码中删除一些反模式,希望您不介意。请检查我添加的 cmets。我还添加了 withRouter 以更好地说明模式

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);

【讨论】:

这里也不需要 &lt;div&gt; .. 如果没有要渲染的内容,您可以返回 null,当然这取决于页面的布局 另一种反模式是import * as React from "react";,它将被打破。 OP 导入更好,但仍然损坏。 但你知道,我实际上不会将侧边栏变成容器。将 FileTree 变成一个容器并将侧边栏作为一个展示布局组件可能更合乎逻辑,如果感觉更合乎逻辑的话。因为 FileTree 做事,调度动作等等。侧边栏只是出现和消失,为什么还要承担所有的负载。稍后您的侧边栏中可能会有其他组件(例如用户栏),并且侧边栏将变得越来越智能,直到您必须重构所有内容。反之亦然 @Aluan Haddad ,您的建议称为合成导入,并且仅在以这种方式设置 tsconfig 时才有效。如果您查看官方页面typescriptlang.org/docs/handbook/react-&-webpack.html,您会看到它是这样完成的 不用担心,谢谢你的链接,我会更仔细地调查,不幸的是我已经早上 5 点了祝你好运!

以上是关于强烈键入 react-redux 与 typescript 连接的主要内容,如果未能解决你的问题,请参考以下文章

Typescript 无状态 HOC react-redux 注入道具打字

React-redux框架之connect()与Provider组件 用法讲解

将 Typescript 与 React-Redux 一起使用时出现类型错误

React 15 与 React-redux 不兼容

React全家桶React-Redux

redux与react-redux