【问题标题】:How to use TypeScript with withRouter, connect, React.Component and custom properties?如何将 TypeScript 与 withRouter、connect、React.Component 和自定义属性一起使用?
【发布时间】:2018-08-07 09:31:33
【问题描述】:

我有一个使用 connectwithRouter 并接收自定义属性的 React Component。我正在尝试将其转换为 TypeScript,我想知道我是否正确执行此操作。至少,我现在没有错误。

这是显示概念的代码:

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

组件是这样使用的:&lt;MyTest className={"active"} /&gt;

我必须做很多实验才能让它发挥作用。例如:

1) 当我像这样省略 withRouter 的类型时: export default withRouter(connect... 然后我得到TS2339: Property 'className' does not exist on type 'IntrinsicAttributes &amp; IntrinsicClassAttributes&lt;Component&lt;Pick&lt;RouteComponentProps&lt;any&gt;, never&gt;, C...'. 不知何故,这里有人建议:React router in TypeScript- both router and own props,尽管我不了解这个概念。

2) 如果您想知道最后一行 as any,这与 https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18999 有关,没有它我会收到此错误:

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

那么这是正确的做法吗?你在哪里看到问题?我基本上使用的是所有最新版本,这是我的 package.json 中的一个 sn-p:

"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",

【问题讨论】:

  • 哦,我什至根本没有安装@types :)

标签: reactjs typescript react-router react-redux typescript2.0


【解决方案1】:

我在我们的项目中是怎么做的(这是最简单的方法,你用的时候就可以得到智能感知):

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 extends RouteComponentProps {      // custom properties passed to component
  className: string;
}

type PropsType = StateProps & DispatchProps & Props;


class MyTest extends React.Component<PropsType> {
  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, ownProps: Props): StateProps => ({
  testList: state.myList,   // todo: define a type for the root state to have validation here
});

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

export default withRouter(connect(
  mapStateToProps,
  dispatchToProps,
)(MyTest));

此外,如果这是您经常输入的内容,我建议您编写自定义 sn-p(如果您使用的是 VS Code 之类的东西,这很容易)。

【讨论】:

    【解决方案2】:

    我尝试重写您的示例并最终得到以下代码:

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

    我已将 export default 更改为使用 connectwithRouter HOC 将包装的 MyTestComponent 类的结果分配到我的测试。然后我像这样导入 MyTest 组件

    import { MyTest } from './MyTest' 
    

    我添加了接口来描述从父组件传递的所有属性,还以不同的方式使用 withRouterconnect(对我来说更具可读性)。

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      我通过强制安装 @types/react-redux 包解决了这个问题。我刚刚从 4.4.5 更新到 5.0.15。

      运行一个新的npm install --save @types/react-redux@5.0.15 可能是值得的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多