web-dev-qa-db-ja.com

TypeScriptとReact-Router 4および5を使用して保護/プライベートルートを書き換える方法

TypeScriptを使用して、react-router documents で説明されているように<PrivateRoute>を作成しようとしました。誰か助けてもらえますか?

react-routerドキュメントのprivateRoute:

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

以下は私のTypeScriptバージョンです(機能しません):

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 }
                }} />
            )
    )} />
}

<React.Component {...thisProps} />は正しくありません。エラーは次のとおりです:NodeInvocationException:inst.render is not a function TypeError:inst.render is not a function

25
Charlie

おそらく、エラーはタイピングとレンダリングの暗黙的な戻りに関係しています。これを修正すると、最終的には次のようになります。

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

このコンポーネントは次のように使用できます。

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

上記のソリューションにはいくつかの欠点があります。 1つは、型の安全性を失うことです。

おそらくRouteコンポーネントを拡張することをお勧めします。

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}/>;
        }
    }
}

したがって、次のようなコンポーネントを使用できます。

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

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

アップデート(2019年11月)

機能コンポーネントを作成したい場合は、非常によく似た方法で作成できます。これはReactルーター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;

アップデート(2019年12月)

ユーザーが最初にアクセスしたいパスにユーザーをリダイレクトする場合は、パスを覚えておく必要があります。これにより、認証が成功した後にリダイレクトできます。次の答えはそれを通してあなたを導きます:

react-router-domによる認証が成功した後、ユーザーをリクエストしたページにリダイレクトします

51
Robin

少しすっきりしたSFCフォームを引き続き使用できます。必要な小道具を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