在Typescript项目中实现react-router PrivateRoute

时间:2018-11-01 15:17:53

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

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

连接器/专用路由

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;

组件/专用路由

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>>; }'.

4 个答案:

答案 0 :(得分:3)

发生错误是因为PrivateRouteProps具有必需的属性location,而您在PrivateRoute中使用components/Routes.tsx时没有提供。我假设此位置应该来自路由器自动传递到路由的routeProps功能的render,就像在原始示例中所做的那样。解决此问题后,就会出现另一个错误:components/Routes.tsx传递了paths中未声明的PrivateRouteProps属性。由于PrivateRoute正在将它不知道的任何道具传递给Route,因此PrivateRouteProps应该从RouteProps扩展react-router,以便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;

答案 1 :(得分:1)

当前答案有效,但是我想发布我的解决方案,因为我认为它具有一些优点:

示例:

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

答案 2 :(得分:0)

我发现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;

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

答案 3 :(得分:-1)

我将“ React.ReactNode”类型用于子组件,而不是任何子组件。