【问题标题】:Implement react-router PrivateRoute in Typescript project在 Typescript 项目中实现 react-router PrivateRoute
【发布时间】:2019-04-05 20:20:49
【问题描述】:

以下是 react-router 的示例,说明如何为受保护的路由添加组件:

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

https://reacttraining.com/react-router/web/example/auth-workflow

我已尝试在我的 Typescript 项目中实现此功能,并以上面的示例为灵感。

组件/路线

import PrivateRoute from '../../connectors/PrivateRoute';
<PrivateRoute path="/codes" component={SomePage} />

连接器/PrivateRoute

import { connect } from 'react-redux';
import { AppState } from 'app-types';
import PrivateRouteComponent from '../../components/PrivateRoute';

const mapStateToProps = (state: AppState) => {
    const isSignedIn = state.user.isSignedIn;

    return {
        isSignedIn
    };
};

const PrivateRoute = connect(
    mapStateToProps,
    null
)(PrivateRouteComponent);

export default PrivateRoute;

组件/PrivateRoute

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

interface PrivateRouteProps {
    // tslint:disable-next-line:no-any
    component: any;
    isSignedIn: boolean;
    // tslint:disable-next-line:no-any
    location: any;
}

const PrivateRoute = (props: PrivateRouteProps) => {
    const { component: Component, isSignedIn, location, ...rest } = props;

    return (
        <Route
            {...rest}
            render={(routeProps) =>
                isSignedIn ? (
                    <Component {...routeProps} />
                ) : (
                        <Redirect
                            to={{
                                pathname: '/signin',
                                state: { from: location }
                            }}
                        />
                    )
            }
        />
    );
};

export default PrivateRoute;

错误

(105,18): Type '{ path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; }' is not assignable to type 'Readonly<Pick<PrivateRouteProps, "location" | "component">>'.
  Property 'location' is missing in type '{ path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; }'.

【问题讨论】:

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


    【解决方案1】:

    发生该错误是因为PrivateRouteProps 有一个必需的属性location,当您在components/Routes.tsx 中使用PrivateRoute 时未提供该属性。我假设这个位置应该来自路由器自动传递给路由的render 函数的routeProps,就像在原始示例中所做的那样。修复此问题后,会出现另一个错误:components/Routes.tsx 正在传递未在PrivateRouteProps 中声明的paths 属性。由于PrivateRoute 将任何它不知道的道具传递给RoutePrivateRouteProps 应该从react-router 扩展RouteProps,以便PrivateRoute 接受Route 接受的所有道具。

    这里是components/PrivateRoute.tsx 两次修复后:

    import * as React from 'react';
    import {
        Route,
        Redirect,
        RouteProps,
    } from 'react-router-dom';
    
    interface PrivateRouteProps extends RouteProps {
        // tslint:disable-next-line:no-any
        component: any;
        isSignedIn: boolean;
    }
    
    const PrivateRoute = (props: PrivateRouteProps) => {
        const { component: Component, isSignedIn, ...rest } = props;
    
        return (
            <Route
                {...rest}
                render={(routeProps) =>
                    isSignedIn ? (
                        <Component {...routeProps} />
                    ) : (
                            <Redirect
                                to={{
                                    pathname: '/signin',
                                    state: { from: routeProps.location }
                                }}
                            />
                        )
                }
            />
        );
    };
    
    export default PrivateRoute;
    

    【讨论】:

    • 这太棒了!如果我可能会问一个后续问题:我为什么需要使用 RouteProps 扩展 PrivateRouteProps,而我通常不需要在我的其他组件中这样做?
    • 在这种情况下扩展RouteProps 的目的是接受path 属性,以便可以将其传递给Route。如果您正在谈论的其他组件包装Route 并以与PrivateRoute 相同的方式传递道具,我希望它们的道具类型需要扩展RouteProps;如果这些组件不包装Route,那么RouteProps 不相关。
    【解决方案2】:

    我发现Matt's answer 非常有用,但需要它为childrencomponent 工作,所以调整如下:

    import * as React from 'react';
    import { Route, Redirect, RouteProps } from 'react-router-dom';
    import { fakeAuth } from '../api/Auth';
    
    interface PrivateRouteProps extends RouteProps {
      // tslint:disable-next-line:no-any
      component?: any;
      // tslint:disable-next-line:no-any
      children?: any;
    }
    
    const PrivateRoute = (props: PrivateRouteProps) => {
      const { component: Component, children, ...rest } = props;
    
      return (
        <Route
          {...rest}
          render={routeProps =>
            fakeAuth.isAuthenticated ? (
              Component ? (
                <Component {...routeProps} />
              ) : (
                children
              )
            ) : (
              <Redirect
                to={{
                  pathname: '/signin',
                  state: { from: routeProps.location },
                }}
              />
            )
          }
        />
      );
    };
    
    export default PrivateRoute;
    

    注意:这恰好是使用 fakeAuth 就像原来的 training article 而不是 user1283776 的 isSignedIn redux 的东西,但你明白了。

    【讨论】:

      【解决方案3】:

      目前的答案有效,但我想发布我的解决方案,因为我认为它有几个优点:

      例子:

      import * as React from 'react';
      import { connect, ConnectedProps } from 'react-redux';
      import {
          Redirect,
          Route,
          RouteProps,
      } from 'react-router-dom';
      import { AppState } from '../store';
      
      const mapState = (state: AppState) => ({
        loggedIn: state.system.loggedIn,
      });
      
      const connector = connect(
        mapState,
        { }
      );
      
      type PropsFromRedux = ConnectedProps<typeof connector>;
      
      type Props = PropsFromRedux & RouteProps & {
      
      };
      
      const PrivateRoute: React.FC<Props> = props => {
          const { loggedIn, ...rest } = props;
      
          return ( !loggedIn ? <Redirect to="/login/" /> :
            <Route {...rest} />
          );
      };
      
      export default connector(PrivateRoute);
      

      【讨论】:

        【解决方案4】:

        我对子组件使用“React.ReactNode”类型而不是任何类型。

        【讨论】:

        • 您好,欢迎来到 StackOverflow!请考虑更新您的答案,以更清楚和具体地了解在答案中发布的原始代码中要修改的内容。考虑修改代码并直接发布更新和工作代码,或更改的特定行的前后,以及它们来自哪个文件的引用。这将大大改善这个答案。
        猜你喜欢
        • 2020-08-25
        • 2019-07-02
        • 2019-03-22
        • 1970-01-01
        • 2020-12-20
        • 2020-06-06
        • 2020-11-12
        • 2020-11-02
        • 1970-01-01
        相关资源
        最近更新 更多