web-dev-qa-db-de.com

Wie schreibe ich die geschützte / private Route mit TypeScript und React-Router 4 und 5 um?

Ich habe versucht, ein <PrivateRoute> wie im React-Router beschrieben Dokumente mit TypeScript. Kann mir jemand helfen?

Die privateRoute im React-Router-Dokument:

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

Unten ist meine TypeScript-Version (es wird nicht funktionieren):

const PrivateRoute = (theProps: { path: string, component: React.SFC<RouteComponentProps<any> | undefined> | React.ComponentClass<RouteComponentProps<any> | undefined> }) => {
    return <Route path={theProps.path} render={props => (
        fakeAuth.isAuthenticated ? (
            <React.Component {...theProps} /> <!-- **** It will raise error *** -->
        ) : (
                <Redirect to={{
                    pathname: '/',
                    state: { from: props.location }
                }} />
            )
    )} />
}

Das <React.Component {...thisProps} /> stimmt nicht. Der Fehler lautet: NodeInvocationException: inst.render ist keine Funktion TypeError: inst.render ist keine Funktion

25
Charlie

Wahrscheinlich hat der Fehler mit der Eingabe und der impliziten Rückgabe beim Rendern zu tun. Wenn Sie dies beheben, gelangen Sie letztendlich zu so etwas:

const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
    const routeComponent = (props: any) => (
        isAuthenticated
            ? React.createElement(component, props)
            : <Redirect to={{pathname: '/login'}}/>
    );
    return <Route {...rest} render={routeComponent}/>;
};

Diese Komponente kann folgendermaßen verwendet werden:

<PrivateRoute
    path='/private'
    isAuthenticated={this.props.state.session.isAuthenticated}
    component={PrivateContainer}
/>

Bei der obigen Lösung gibt es einige Nachteile. Eine davon ist, dass Sie die Typensicherheit verlieren.

Wahrscheinlich ist die Erweiterung der Komponente Route die bessere Idee.

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

export interface ProtectedRouteProps extends RouteProps {
    isAuthenticated: boolean;
    authenticationPath: string;
}

export class ProtectedRoute extends Route<ProtectedRouteProps> {
    public render() {
        let redirectPath: string = '';
        if (!this.props.isAuthenticated) {
            redirectPath = this.props.authenticationPath;
        }

        if (redirectPath) {
            const renderComponent = () => (<Redirect to={{pathname: redirectPath}}/>);
            return <Route {...this.props} component={renderComponent} render={undefined}/>;
        } else {
            return <Route {...this.props}/>;
        }
    }
}

Sie können die Komponente also folgendermaßen verwenden:

const defaultProtectedRouteProps: ProtectedRouteProps = {
    isAuthenticated: this.props.state.session.isAuthenticated,
    authenticationPath: '/login',
};

<ProtectedRoute
    {...defaultProtectedRouteProps}
    exact={true}
    path='/'
    component={ProtectedContainer}
/>

Update (November 2019)

Wenn Sie lieber Funktionskomponenten schreiben möchten, können Sie dies auf sehr ähnliche Weise tun. Dies funktioniert auch mit React Router 5:

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

export interface ProtectedRouteProps extends RouteProps {
  isAuthenticated: boolean;
  isAllowed: boolean;
  restrictedPath: string;
  authenticationPath: string;
}

export const ProtectedRoute: React.FC<ProtectedRouteProps> = props => {
  let redirectPath = '';
  if (!props.isAuthenticated) {
    redirectPath = props.authenticationPath;
  }
  if (props.isAuthenticated && !props.isAllowed) {
    redirectPath = props.restrictedPath;
  }

  if (redirectPath) {
    const renderComponent = () => <Redirect to={{ pathname: redirectPath }} />;
    return <Route {...props} component={renderComponent} render={undefined} />;
  } else {
    return <Route {...props} />;
  }
};

export default ProtectedRoute;

Update (Dezember 2019)

Wenn Sie einen Benutzer auf den Pfad umleiten möchten, auf den der Benutzer zuerst zugreifen wollte, müssen Sie sich den Pfad merken, damit Sie nach erfolgreicher Authentifizierung umleiten können. Die folgende Antwort führt Sie durch das:

Leitet einen Benutzer auf die Seite um, die er nach erfolgreicher Authentifizierung mit react-router-dom angefordert hat

51
Robin

Sie können immer noch das SFC-Formular verwenden, das ich etwas sauberer finde. Mischen Sie einfach alle benötigten Requisiten mit dem RouteProps:

const PrivateRoute: React.SFC<RouteProps> = ({
  component: Component,
  ...rest
}: {
  component: React.ComponentType<RouteProps>;
}) => (
  <Route
    {...rest}
    render={props =>
      fakeAuth.isAuthenticated 
        ? <Component {...props} /> 
        : <Redirect to="/login" />
    }
  />
);
5
Hunter McMillen