我试图用TypeScript创建一个<PrivateRoute>,如react router documents中所述.有人能帮我吗?

The privateRoute in react-router document:

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

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

<React.Component {...thisProps} />是不对的.错误是:NodeInvocationException:inst.render不是函数

推荐答案

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

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

该组件可以这样使用:

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

上面的解决方案有一些缺点.其中一个问题是你失go 了类型安全性.

也许扩展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}/>;
        }
    }
}

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

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

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

更新(2019年11月)

如果你喜欢写功能组件,你可以用一种非常相似的方式.这也适用于React路由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月)

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

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

更新(损坏2021)

上述解决方案有点过时.ProtectedRoute组件可以简单地编写如下:

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路由V6,则需要将Redirect替换为Navigate.以下是重定向到最初请求的页面的完整示例:

更新(2022年1月)

由于<Routes>岁的子元素需要有<Route>个元素,所以<ProtectedRoute>个元素可以更改为:

export type ProtectedRouteProps = {
  isAuthenticated: boolean;
  authenticationPath: string;
  outlet: JSX.Element;
};

export default function ProtectedRoute({isAuthenticated, authenticationPath, outlet}: ProtectedRouteProps) {
  if(isAuthenticated) {
    return outlet;
  } else {
    return <Navigate to={{ pathname: authenticationPath }} />;
  }
};

<ProtectedRoute>现在可以按如下方式应用:

const defaultProtectedRouteProps: Omit<ProtectedRouteProps, 'outlet'> = {
  isAuthenticated: !!sessionContext.isAuthenticated,
  authenticationPath: '/login',
};

return (
  <div>
    <Routes>
      <Route path='/' element={<Homepage />} />
      <Route path='dashboard' element={<ProtectedRoute {...defaultProtectedRouteProps} outlet={<Dashboard />} />} />
      <Route path='protected' element={<ProtectedRoute {...defaultProtectedRouteProps} outlet={<Protected />} />} />
      <Route path='nested' element={<ProtectedRoute {...defaultProtectedRouteProps} outlet={<Layout />} />}>
        <Route path='one' element={<Protected />} />
        <Route path='two' element={<Protected />} />
      </Route>
      <Route path='login' element={<Login />} />
    </Routes>
  </div>
);

我还更新了React Router 6 example.到目前为止,甚至有一个官方指南:https://reactrouter.com/docs/en/v6/examples/auth

Typescript相关问答推荐

在角形加载组件之前无法获取最新令牌

如何正确修复TypScript 4.7到4.8升级中的TS 2345编译错误

忽略和K的定义

键集分页顺序/时间戳上的筛选器,精度不相同

Angular 信号:当输入信号改变值时,S触发取数的正确方式是什么?

类型脚本`Return;`vs`Return unfined;`

使用2个泛型类型参数透明地处理函数中的联合类型

推断从其他类型派生的类型

具有自引用属性的接口

为什么&;(交集)运算符会删除不相交的属性?

如何在React组件中指定forwardRef是可选的?

在正常函数内部调用异步函数

如何连接属性名称和值?

是否将Angular *ngFor和*ngIf迁移到新的v17语法?

如何创建一个将嵌套类属性的点表示法生成为字符串文字的类型?

如何使用警告实现`RecursiveReadonly<;T>;`,如果`T`是原语,则返回`T`而不是`RecursiveReadonly<;T>;`

Route.ts文件中未导出HTTP方法

如何创建允许使用带有联合类型参数的Array.from()的TS函数?

为什么 TypeScript 类型条件会影响其分支的结果?

递归类型名称