【问题标题】: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 课程也会很有帮助,谢谢。

【问题讨论】:

    标签: reactjs typescript react-router react-router-v4


    【解决方案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,但不会在应用程序运行之前提供静态类型的好处,例如自动完成/智能感知/错误。
          猜你喜欢
          • 2020-06-13
          • 1970-01-01
          • 2021-05-24
          • 2019-01-18
          • 2017-10-30
          • 2018-09-29
          • 2019-01-20
          • 1970-01-01
          • 2020-06-03
          相关资源
          最近更新 更多