如何使用 TypeScript 和 React-Router 4、5 或 6 重写受保护/私有路由? [英] How to rewrite the protected/private route using TypeScript and React-Router 4, 5 or 6?

查看:40
本文介绍了如何使用 TypeScript 和 React-Router 4、5 或 6 重写受保护/私有路由?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个 <PrivateRoute> 如 react-router 文档 使用 TypeScript.谁能帮帮我?

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

react-router 文档中的 privateRoute:

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

以下是我的 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 }
                }} />
            )
    )} />
}

不对.错误是:NodeInvocationException: inst.render is not a function类型错误:inst.render 不是函数

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

推荐答案

该错误可能与键入和渲染中的隐式返回有关.当你解决这个问题时,你最终会变成这样:

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.

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

Probably extending the Route component is the better idea.

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

更新(2019 年 11 月)

如果你更喜欢编写函数式组件,你可以用非常相似的方式来完成.这也适用于 React Router 5:

Update (Nov 2019)

If you prefer to write functional components you can do it in a very similar manner. This also works with 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;

更新(2019 年 12 月)

如果要将用户重定向到用户想先访问的路径,需要记住路径,这样才能在认证成功后重定向.以下答案将指导您完成此操作:

Update (Dec 2019)

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:

重定向用户通过 react-router-dom 成功认证后访问他们请求的页面

上面的解决方案有点过时了.ProtectedRoute 组件可以简单地写成如下:

The solution above is a bit outdated. The ProtectedRoute component can simply be written as follows:

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

export type ProtectedRouteProps = {
  isAuthenticated: boolean;
  authenticationPath: string;
} & RouteProps;

export default function ProtectedRoute({isAuthenticated, authenticationPath, ...routeProps}: ProtectedRouteProps) {
  if(isAuthenticated) {
    return <Route {...routeProps} />;
  } else {
    return <Redirect to={{ pathname: authenticationPath }} />;
  }
};

如果您使用 React Router V6,则需要将 Redirect 替换为 Navigate.可以在此处找到重定向到原始请求页面的完整示例:

If you use React Router V6 you need to replace Redirect with Navigate. A full example with redirection to the originally requested page can be found here:

这篇关于如何使用 TypeScript 和 React-Router 4、5 或 6 重写受保护/私有路由?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆