如何使用 Typescript 创建连接到 Redux 的高阶 React 组件



我需要创建一个 React 高阶组件来保护应用程序中的路由免受没有访问令牌的用户的侵害。然后使用该 HOC 将组件包装在父组件上,如下所示:

<Route
    exact
    path={INDEX_URL}
    render={isAuthenticated((props: any) => (<LandingPage {...props} />))}
/>

我正在使用打字稿,并且我在连接功能和react-router-dom的路由器方面遇到了一些问题。

请问有人可以帮我吗?谢谢你,祝你周末愉快!😀

我已经尝试扩展我的接口与RouterProps和/或路由器,但没有一个有效。这似乎是一些与打字相关的问题,但我无法弄清楚它到底是什么。

import * as React from 'react';
import { Redirect, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import * as H from 'history';
// Constants
import { LOGIN_URL } from '../../../data/constants/index.constants';
interface IAuthenticatedProps {
    accessToken: string | null;
    location: H.Location;
}
interface IAuthenticatedState {
    isAuthenticated: boolean | undefined;
}
/**
 * @description Higher-order component (HOC) to wrap Authenticated pages
 * @date 2019-01-18
 * @class Authenticated
 * @extends {React.Component<IAuthenticatedProps, any>}
 */
const isAuthenticated = (ComposedComponent: any) => {
    class Authenticated extends React.Component<
        IAuthenticatedProps,
        IAuthenticatedState
    > {
(...)
    function mapStateToProps(state: any) {
        return {
            accessToken: state.authentication.accessToken,
        };
    }
    return withRouter(connect(mapStateToProps)(Authenticated));
};
export default isAuthenticated;

我预计没有问题,但 Typescript 给了我这个:

[ts]
Argument of type 'ConnectedComponentClass<typeof Authenticated, Pick<IAuthenticatedProps, "location">>' is not assignable to parameter of type 'ComponentType<RouteComponentProps<any, StaticContext, any>>'.
  Type 'ConnectedComponentClass<typeof Authenticated, Pick<IAuthenticatedProps, "location">>' is not assignable to type 'ComponentClass<RouteComponentProps<any, StaticContext, any>, any>'.
    Type 'Component<Pick<IAuthenticatedProps, "location">, any, any>' is not assignable to type 'Component<RouteComponentProps<any, StaticContext, any>, any, any>'.
      Types of property 'props' are incompatible.
        Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<IAuthenticatedProps, "location">>' is not assignable to type 'Readonly<{ children?: ReactNode; }> & Readonly<RouteComponentProps<any, StaticContext, any>>'.
          Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<IAuthenticatedProps, "location">>' is missing the following properties from type 'Readonly<RouteComponentProps<any, StaticContext, any>>': history, match [2345]

withRouter()具有为其包装的组件提供道具的效果。因此,为了使用它,您要包装的组件必须期望这些道具。在这种情况下,Authenticated只期待IAuthenticatedProps,但withRouter()提供了更多。所以TypeScript之所以抱怨,是因为withRouter()试图为Authenticated没有说出它所期望的Authenticated提供道具。

withRouter()提供的道具在接口RouteComponentProps中定义。尝试将其添加到您的道具类型中,以便Authenticated

  class Authenticated extends React.Component<
    IAuthenticatedProps & RouteComponentProps<{}>,
    IAuthenticatedState
  > {
    // ...
  }

您还可以从IAuthenticatedProps界面中删除location,因为这也包含在RouteComponentProps界面中。

另一个问题是Route中的render道具需要一个函数,而不是任何旧的 React 组件类型。由于isAuthenticated()返回一个React.ComponentClass,因此您需要执行以下操作:

const Authenticated = isAuthenticated(LandingPage);
// ...
<Route
  exact
  path={INDEX_URL}
  render={(props: any) => <Authenticated {...props} />}
/>

最新更新