如何使用 TypeScript 和 React-Router 4 和 5 重写受保护/私有路由?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47747754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:06:23  来源:igfitidea点击:

How to rewrite the protected/private route using TypeScript and React-Router 4 and 5?

reactjstypescriptreact-routertypescript2.0react-router-v4

提问by Charlie

I was trying to create a <PrivateRoute>as describe in the react-router documentsusing TypeScript. Can anyone help me out?

我试图使用 TypeScript<PrivateRoute>在 react-router文档中创建一个描述。谁能帮我吗?

The privateRoute in react-router document:

react-router 文档中的 privateRoute:

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    fakeAuth.isAuthenticated ? (
      <Component {...props}/>
    ) : (
      <Redirect to={{pathname: '/login', state: { from: props.location }
   }}/>
  )
 )}/>
)

Below is my TypeScript version(it won't work) :

下面是我的 TypeScript 版本(它不起作用):

const PrivateRoute = (theProps: { path: string, component: React.SFC<RouteComponentProps<any> | undefined> | React.ComponentClass<RouteComponentProps<any> | undefined> }) => {
    return <Route path={theProps.path} render={props => (
        fakeAuth.isAuthenticated ? (
            <React.Component {...theProps} /> <!-- **** It will raise error *** -->
        ) : (
                <Redirect to={{
                    pathname: '/',
                    state: { from: props.location }
                }} />
            )
    )} />
}

The <React.Component {...thisProps} />is not right. The error is: NodeInvocationException: inst.render is not a function TypeError: inst.render is not a function

<React.Component {...thisProps} />是不对的。错误是:NodeInvocationException: inst.render is not a function TypeError: inst.render is not a function

回答by Robin

Probably the error has to do with the typing and the implicit return in rendering. When you fix this you get ultimately to something like this:

错误可能与打字和渲染中的隐式返回有关。当你解决这个问题时,你最终会得到这样的结果:

const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
    const routeComponent = (props: any) => (
        isAuthenticated
            ? React.createElement(component, props)
            : <Redirect to={{pathname: '/login'}}/>
    );
    return <Route {...rest} render={routeComponent}/>;
};

This component can be used like this:

该组件可以这样使用:

<PrivateRoute
    path='/private'
    isAuthenticated={this.props.state.session.isAuthenticated}
    component={PrivateContainer}
/>

There are a few draw backs with the solution above. One of the is that you lose type safety.

上述解决方案有一些缺点。其中之一是你失去了类型安全。

Probably extending the Routecomponent is the better idea.

可能扩展Route组件是更好的主意。

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

export interface ProtectedRouteProps extends RouteProps {
    isAuthenticated: boolean;
    authenticationPath: string;
}

export class ProtectedRoute extends Route<ProtectedRouteProps> {
    public render() {
        let redirectPath: string = '';
        if (!this.props.isAuthenticated) {
            redirectPath = this.props.authenticationPath;
        }

        if (redirectPath) {
            const renderComponent = () => (<Redirect to={{pathname: redirectPath}}/>);
            return <Route {...this.props} component={renderComponent} render={undefined}/>;
        } else {
            return <Route {...this.props}/>;
        }
    }
}

So you can use the component like this:

所以你可以像这样使用组件:

const defaultProtectedRouteProps: ProtectedRouteProps = {
    isAuthenticated: this.props.state.session.isAuthenticated,
    authenticationPath: '/login',
};

<ProtectedRoute
    {...defaultProtectedRouteProps}
    exact={true}
    path='/'
    component={ProtectedContainer}
/>

Update (Nov 2019)

更新(2019 年 11 月)

If you prefer to write functional components you can do it in a very similar manner. This also works with React Router 5:

如果您更喜欢编写功能组件,您可以以非常相似的方式来完成。这也适用于 React Router 5:

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

export interface ProtectedRouteProps extends RouteProps {
  isAuthenticated: boolean;
  isAllowed: boolean;
  restrictedPath: string;
  authenticationPath: string;
}

export const ProtectedRoute: React.FC<ProtectedRouteProps> = props => {
  let redirectPath = '';
  if (!props.isAuthenticated) {
    redirectPath = props.authenticationPath;
  }
  if (props.isAuthenticated && !props.isAllowed) {
    redirectPath = props.restrictedPath;
  }

  if (redirectPath) {
    const renderComponent = () => <Redirect to={{ pathname: redirectPath }} />;
    return <Route {...props} component={renderComponent} render={undefined} />;
  } else {
    return <Route {...props} />;
  }
};

export default ProtectedRoute;

Update (Dec 2019)

更新(2019 年 12 月)

If you want to redirect a user to the path the user wanted to access first, you need to remember the path, so you can redirect after successful authentication. The following answer will guide you through that:

如果要将用户重定向到用户首先要访问的路径,则需要记住该路径,以便在认证成功后重定向。以下答案将指导您完成:

Redirecting a user to the page they requested after successful authentication with react-router-dom

使用 react-router-dom 成功验证后将用户重定向到他们请求的页面

回答by Hunter McMillen

You can still use the SFC form, which I find a little cleaner. Just mix in any props you need with the RouteProps:

您仍然可以使用 SFC 表单,我觉得它更简洁一些。只需将您需要的任何道具与RouteProps

const PrivateRoute: React.SFC<RouteProps> = ({
  component: Component,
  ...rest
}: {
  component: React.ComponentType<RouteProps>;
}) => (
  <Route
    {...rest}
    render={props =>
      fakeAuth.isAuthenticated 
        ? <Component {...props} /> 
        : <Redirect to="/login" />
    }
  />
);

回答by George

My PrivateRoute

我的私人路线

import React from 'react'
import {Redirect, Route, RouteProps} from 'react-router'

export interface IPrivateRouteProps extends RouteProps {
  isAuth: boolean // is authenticate route
  redirectPath: string // redirect path if don't authenticate route
}

const PrivateRoute: React.FC<IPrivateRouteProps> = (props) => {
   return props.isAuth ? (
    <Route {...props} component={props.component} render={undefined} />
  ) : (
    <Redirect to={{pathname: props.redirectPath}} />
  )
}

export default PrivateRoute

Using

使用

<PrivateRoute isAuth={false} redirectPath="/login" path="/t1">
  <Pages.Profile /> your`s protected page
</PrivateRoute>