在 Typescript 项目中实现 react-router PrivateRoute

Posted

技术标签:

【中文标题】在 Typescript 项目中实现 react-router PrivateRoute【英文标题】:Implement react-router PrivateRoute in Typescript project 【发布时间】:2019-04-05 20:20:49 【问题描述】:

以下是 react-router 的示例,说明如何为受保护的路由添加组件:

function PrivateRoute( component: Component, ...rest ) 
  return (
    <Route
      ...rest
      render=props =>
        fakeAuth.isAuthenticated ? (
          <Component ...props />
        ) : (
          <Redirect
            to=
              pathname: "/login",
              state:  from: props.location 
            
          />
        )
      
    />
  );

https://reacttraining.com/react-router/web/example/auth-workflow

我已尝试在我的 Typescript 项目中实现此功能,并以上面的示例为灵感。

组件/路线

import PrivateRoute from '../../connectors/PrivateRoute';
<PrivateRoute path="/codes" component=SomePage />

连接器/PrivateRoute

import  connect  from 'react-redux';
import  AppState  from 'app-types';
import PrivateRouteComponent from '../../components/PrivateRoute';

const mapStateToProps = (state: AppState) => 
    const isSignedIn = state.user.isSignedIn;

    return 
        isSignedIn
    ;
;

const PrivateRoute = connect(
    mapStateToProps,
    null
)(PrivateRouteComponent);

export default PrivateRoute;

组件/PrivateRoute

import * as React from 'react';
import 
    Route,
    Redirect,
 from 'react-router-dom';

interface PrivateRouteProps 
    // tslint:disable-next-line:no-any
    component: any;
    isSignedIn: boolean;
    // tslint:disable-next-line:no-any
    location: any;


const PrivateRoute = (props: PrivateRouteProps) => 
    const  component: Component, isSignedIn, location, ...rest  = props;

    return (
        <Route
            ...rest
            render=(routeProps) =>
                isSignedIn ? (
                    <Component ...routeProps />
                ) : (
                        <Redirect
                            to=
                                pathname: '/signin',
                                state:  from: location 
                            
                        />
                    )
            
        />
    );
;

export default PrivateRoute;

错误

(105,18): Type ' path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; ' is not assignable to type 'Readonly<Pick<PrivateRouteProps, "location" | "component">>'.
  Property 'location' is missing in type ' path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; '.

【问题讨论】:

【参考方案1】:

发生该错误是因为PrivateRouteProps 有一个必需的属性location,当您在components/Routes.tsx 中使用PrivateRoute 时未提供该属性。我假设这个位置应该来自路由器自动传递给路由的render 函数的routeProps,就像在原始示例中所做的那样。修复此问题后,会出现另一个错误:components/Routes.tsx 正在传递未在PrivateRouteProps 中声明的paths 属性。由于PrivateRoute 将任何它不知道的道具传递给RoutePrivateRouteProps 应该从react-router 扩展RouteProps,以便PrivateRoute 接受Route 接受的所有道具。

这里是components/PrivateRoute.tsx 两次修复后:

import * as React from 'react';
import 
    Route,
    Redirect,
    RouteProps,
 from 'react-router-dom';

interface PrivateRouteProps extends RouteProps 
    // tslint:disable-next-line:no-any
    component: any;
    isSignedIn: boolean;


const PrivateRoute = (props: PrivateRouteProps) => 
    const  component: Component, isSignedIn, ...rest  = props;

    return (
        <Route
            ...rest
            render=(routeProps) =>
                isSignedIn ? (
                    <Component ...routeProps />
                ) : (
                        <Redirect
                            to=
                                pathname: '/signin',
                                state:  from: routeProps.location 
                            
                        />
                    )
            
        />
    );
;

export default PrivateRoute;

【讨论】:

这太棒了!如果我可能会问一个后续问题:我为什么需要使用 RouteProps 扩展 PrivateRouteProps,而我通常不需要在我的其他组件中这样做? 在这种情况下扩展RouteProps 的目的是接受path 属性,以便可以将其传递给Route。如果您正在谈论的其他组件包装Route 并以与PrivateRoute 相同的方式传递道具,我希望它们的道具类型需要扩展RouteProps;如果这些组件不包装Route,那么RouteProps 不相关。【参考方案2】:

我发现Matt's answer 非常有用,但需要它为childrencomponent 工作,所以调整如下:

import * as React from 'react';
import  Route, Redirect, RouteProps  from 'react-router-dom';
import  fakeAuth  from '../api/Auth';

interface PrivateRouteProps extends RouteProps 
  // tslint:disable-next-line:no-any
  component?: any;
  // tslint:disable-next-line:no-any
  children?: any;


const PrivateRoute = (props: PrivateRouteProps) => 
  const  component: Component, children, ...rest  = props;

  return (
    <Route
      ...rest
      render=routeProps =>
        fakeAuth.isAuthenticated ? (
          Component ? (
            <Component ...routeProps />
          ) : (
            children
          )
        ) : (
          <Redirect
            to=
              pathname: '/signin',
              state:  from: routeProps.location ,
            
          />
        )
      
    />
  );
;

export default PrivateRoute;

注意:这恰好是使用 fakeAuth 就像原来的 training article 而不是 user1283776 的 isSignedIn redux 的东西,但你明白了。

【讨论】:

【参考方案3】:

目前的答案有效,但我想发布我的解决方案,因为我认为它有几个优点:

不使用 any 类型覆盖属性 component。 利用 render method from the library 来支持 &lt;Route&gt; 组件和子道具 - 无需重新实现已经存在的框架逻辑/代码。 使用来自官方react-redux 文档的Recipe: Static Typing

例子:

import * as React from 'react';
import  connect, ConnectedProps  from 'react-redux';
import 
    Redirect,
    Route,
    RouteProps,
 from 'react-router-dom';
import  AppState  from '../store';

const mapState = (state: AppState) => (
  loggedIn: state.system.loggedIn,
);

const connector = connect(
  mapState,
   
);

type PropsFromRedux = ConnectedProps<typeof connector>;

type Props = PropsFromRedux & RouteProps & 

;

const PrivateRoute: React.FC<Props> = props => 
    const  loggedIn, ...rest  = props;

    return ( !loggedIn ? <Redirect to="/login/" /> :
      <Route ...rest />
    );
;

export default connector(PrivateRoute);

【讨论】:

【参考方案4】:

我对子组件使用“React.ReactNode”类型而不是任何类型。

【讨论】:

您好,欢迎来到 ***!请考虑更新您的答案,以更清楚和具体地了解在答案中发布的原始代码中要修改的内容。考虑修改代码并直接发布更新和工作代码,或更改的特定行的前后,以及它们来自哪个文件的引用。这将大大改善这个答案。

以上是关于在 Typescript 项目中实现 react-router PrivateRoute的主要内容,如果未能解决你的问题,请参考以下文章

我想在辅助函数中操作 React 类组件中存在的状态。我想在 Typescript 中实现相同的目标

如何将 SignaturePad 与 React TypeScript 一起使用

在 Python 中实现 Typescript 接口

在 Typescript 中实现接口时如何定义私有属性?

使用类组件在 Vue mixin 中实现 typescript 泛型

在 Typescript 中实现 Bull Queue