在打字稿中创建自己的反应路线类

Posted

技术标签:

【中文标题】在打字稿中创建自己的反应路线类【英文标题】:Create own react route class in typescript 【发布时间】:2017-07-07 16:11:59 【问题描述】:

我找到了这个 (reacttraining.com) 站点,它通过一些示例解释了 react-router。但是我不能用打字稿类来做到这一点。我想要做的是扩展 Route 类来构建我自己的。现在我想在 typescript 中实现它以进行身份​​验证,如下面的站点示例所示。

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

我搜索了很多,但找不到解释要实现的功能以及调用嵌套路由的类型属性的站点。 ES6 课程也会很有帮助,谢谢。

【问题讨论】:

【参考方案1】:

这是使用函数组件的一种非常简单的方法,因为新的 react 路由器版本允许您通过钩子访问所有内容:

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

const PrivateRoute = (props: RouteProps) => 
  const  isAuthenticated  = useYourSessionProviderContext()
  if (isAuthenticated) 
    return <Route ...props />
   else 
    return <Redirect to='/login' />
  


export default PrivateRoute


【讨论】:

【参考方案2】:

这里提出的解决方案对我不起作用,因为我在原始路线中同时使用了 componentrender 参数。在此解决方案中,您可以在自定义 PrivateRoute 中使用任何 Route 配置,而不仅仅是 component 参数。

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

interface PrivateRouteProps extends RouteProps 
    isAuthenticated: boolean;


export class PrivateRoute extends Route<PrivateRouteProps> 
    render() 
        return (
            <Route render=(props: RouteComponentProps) => 
                if(!this.props.isAuthenticated()) 
                    return <Redirect to='/login' />
                 

                if(this.props.component) 
                    return React.createElement(this.props.component);
                 

                if(this.props.render) 
                    return this.props.render(props);
                
             />
        );
    

例子:

<PrivateRoute 
    path='/dashboard' 
    component=DashboardPage 
    isAuthenticated=props.isAuthenticated
/>
<PrivateRoute 
    path='/checkout' 
    isAuthenticated=props.isAuthenticated
    render=() => (
       <CheckoutPage auth=props.auth />
    ) 
/>

【讨论】:

尝试使用它,但它从不执行重定向,总是呈现组件。在返回的断点处,我可以看到 this.props.isAuthenticated 为 false,但它仍然呈现组件。 也看到了这个Warning: You should not use &lt;Route component&gt; and &lt;Route render&gt; in the same route; &lt;Route render&gt; will be ignored 好收获。我修改了我的解决方案。【参考方案3】:

这是我使用 "react-router-dom": "^4.4.0-beta.6""typescript": "3.2.2" 的解决方案

import React,  FunctionComponent  from "react";
import 
  Route, 
  Redirect,
  RouteProps, 
  RouteComponentProps
 from "react-router-dom";

interface PrivateRouteProps extends RouteProps 
  component:
    | React.ComponentType<RouteComponentProps<any>>
    | React.ComponentType<any>;


const PrivateRoute: FunctionComponent<PrivateRouteProps> = (
  component: Component,
  ...rest
) => 
  return (
    <Route
      ...rest
      render=props =>
        true ? ( //put your authenticate logic here
          <Component ...props />
        ) : (
          <Redirect
            to=
              pathname: "/signin"
            
          />
        )
      
    />
  );
;

export default PrivateRoute;

【讨论】:

【参考方案4】:

我也在寻找同样的东西。这个问题很老,但也许有人仍在寻找它。 这是我想出的(从 react-router 4 正确使用的所有类型):

interface PrivateRouteProps extends RouteProps 
  component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>

type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;

export class PrivateRoute extends Route<PrivateRouteProps> 
  render () 
    const component: Component, ...rest: PrivateRouteProps = this.props;
    const renderComponent: RenderComponent = (props) => (
      AuthenticationService.isAuthenticated()
        ? <Component ...props />
        : <Redirect to='/login' />
    );

    return (
      <Route ...rest render=renderComponent />
    );
  

【讨论】:

+uno 用于在简洁的解决方案中正确使用类型。【参考方案5】:

关于 Redux ...

Jacka's answer 帮助了我很多,但我很难将PrivateRoute 组件连接到 redux。此外,我想将生成的Route 组件抽象为工作,例如作为LoggedInRouteNotLoggedInRoute 或一般的Route,如果满足条件则显示它的组件,否则会重定向到指定位置:

注意:使用redux 4、react-router-dom 4 和打字稿2.9 编写。

import * as H from 'history';
import * as React from 'react';
import  connect, MapStateToPropsParam  from 'react-redux';
import  Redirect, Route, RouteComponentProps, RouteProps  from 'react-router';

export interface ConditionalRouteProps extends RouteProps 
  routeCondition: boolean;
  redirectTo: H.LocationDescriptor;


export class ConditionalRoute extends React.Component<ConditionalRouteProps> 
  public render() 
    // Extract RouteProps without component property to rest.
    const  component: Component, routeCondition, redirectTo, ...rest  = this.props;
    return <Route ...rest render=this.renderFn />
  

  private renderFn = (renderProps: RouteComponentProps<any>) => 
    if (this.props.routeCondition) 
      const  component: Component  = this.props; // JSX accepts only upprcase.
      if (!Component) 
        return null;
      
      return <Component ...renderProps />
    

    return <Redirect to=this.props.redirectTo />;
  ;


export function connectConditionalRoute<S>(mapStateToProps: MapStateToPropsParam<ConditionalRouteProps, RouteProps, S>) 
  return connect<ConditionalRouteProps, , RouteProps, S>(mapStateToProps)(ConditionalRoute);

您可以使用ConditionalRoute 组件而不连接它并使用组件的本地状态,例如:

interface RootState 
  loggedIn: boolean;


export class Root extends React.Component<RootProps, RootState> 
  /* skipped initialState and setState(...) calls */

  public render() 
    return (
      <Switch>
        <ConditionalRoute
          path="/todos"
          component=TodoPage
          routeCondition=this.state.loggedIn
          redirectTo="/login" />
        <ConditionalRoute
          path="/login"
          component=LoginPage
          routeCondition=!this.state.loggedIn
          redirectTo="/" />
        <Redirect to="/todos" />
      </Switch>
    );
  

或者使用实用函数connectConditionalRoute&lt;S&gt;(...) 来使用你的redux store:

const loginRoute = '/login';
const todosRoute = '/todos';

const LoggedInRoute = connectConditionalRoute<RootState>(state => (
  redirectTo: loginRoute,
  routeCondition: state.isLoggedIn,
));

const NotLoggedInRoute = connectConditionalRoute<RootState>(state => (
  redirectTo: todosRoute,
  routeCondition: !state.isLoggedIn
));

const Root: React.SFC = () => (
  <Switch>
    <LoggedInRoute path="/todos" component=TodoPage />
    <NotLoggedInRoute path="/login" component=LoginPage />
    <Redirect to="/todos" />
  </Switch>
);

提供的示例中的行为:未授权用户访问/todos,被重定向到/login,授权用户访问/login,被重定向到/todos。每当 redux store 的 isLoggedIn 发生变化时,连接的组件都会更新并自动重定向用户。

【讨论】:

【参考方案6】:

这是我目前为止最好的照片,虽然还有一个any :)

import * as React from "react"
import Redirect, Route, RouteComponentProps, RouteProps from "react-router-dom"

type RouteComponent = React.StatelessComponent<RouteComponentProps<>> | React.ComponentClass<any>

const AUTHENTICATED = false // TODO: implement authentication logic

export const PrivateRoute: React.StatelessComponent<RouteProps> = (component, ...rest) => 
  const renderFn = (Component?: RouteComponent) => (props: RouteProps) => 
    if (!Component) 
      return null
    

    if (AUTHENTICATED) 
      return <Component ...props />
    

    const redirectProps = 
      to: 
        pathname: "/auth/sign-in",
        state: from: props.location,
      ,
    

    return <Redirect ...redirectProps />
  

  return <Route ...rest render=renderFn(component) />

【讨论】:

感谢分享您的解决方案!将 PrivateRoute 与具有自己属性的组件一起使用的最佳方式是什么?【参考方案7】:

你可以使用any

const PrivateRoute = (component: Component, ...rest : any) => (
  <Route ...rest render=PrivateRender(Component) />
);

const PrivateRender = (Component: any) => 
  return (props: any) => 
    return <Component ...props/>;
  ;
;

【讨论】:

使用 any 只是关闭 linter,但不会在应用程序运行之前提供静态类型的好处,例如自动完成/智能感知/错误。

以上是关于在打字稿中创建自己的反应路线类的主要内容,如果未能解决你的问题,请参考以下文章

在打字稿中的类中创建具有未知参数的方法

如何在打字稿的泛型类中创建类型为“T”的新对象?

显示路径中的降价文件与打字稿反应

如何在页面加载时在反应打字稿中设置可见性

在打字稿中创建对象

在打字稿中创建随机字符串[重复]