在typescript中创建自己的react路由类

时间:2017-02-18 00:36:53

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

我找到了这个(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课程也会有所帮助,谢谢。

7 个答案:

答案 0 :(得分:18)

到目前为止,这是我最好的一次拍摄,尽管还剩下一个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)} />
}

答案 1 :(得分:6)

关于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<S>(...)来使用您的redux存储:

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存储的isLoggedIn发生更改时,连接的组件都会更新并自动重定向用户。

答案 2 :(得分:3)

我正在寻找相同的东西。这个问题很老,但也许有人还在寻找。 这是我想出的(所有类型都可以从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} />
    );
  }
}

答案 3 :(得分: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 :(得分:2)

您可以使用any

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

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

答案 5 :(得分:0)

此处提出的解决方案对我不起作用,因为我在原始路线中同时使用了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} />
    )} 
/>

答案 6 :(得分:0)

这是使用函数组件的一种非常简单的方法,因为新的 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