Typescript 및 React를 사용하여 Private Route의 구성 요소 소품 전달

Aug 18 2020

React Router v4의 Route 구성 요소의 렌더링 소품을 사용하여 Typescript 및 React로 인증 된 경로를 구현하고 있습니다.

노선 :

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { ROUTES } from 'utils/constants';
import HomePage from 'components/pages/Home';
import GuestLogin from 'components/pages/GuestLogin';
import ProfilePage from 'components/pages/Profile';
import NotFoundPage from 'components/pages/NotFound';
import ResetPassword from 'components/pages/ResetPassword';
import SetPassword from 'components/pages/SetPassword';
import LoginContainer from 'containers/Login';
import PrivateRoute from './PrivateRoute';

const Routes: React.FunctionComponent = () => (
  <Switch>
    <Route path={ROUTES.LOGIN} component={LoginContainer} exact></Route>
    <PrivateRoute
      path={ROUTES.HOME}
      component={HomePage}
    ></PrivateRoute>
    <Route path={ROUTES.GUEST_LOGIN} component={GuestLogin}></Route>
    <Route path={ROUTES.RESET_PASSWORD} component={ResetPassword}></Route>
    <Route path={ROUTES.SET_PASSWORD} component={SetPassword}></Route>
    <Route path={ROUTES.PROFILE} component={ProfilePage}></Route>
    <Route component={NotFoundPage}></Route>
  </Switch>
);

export default Routes;

개인 경로 :

import React from 'react';    
import { useAppContext } from 'containers/App/AppContext';
import { RouteProps, Route, Redirect } from 'react-router-dom';
import { ROUTES } from 'utils/constants';

const PrivateRoute: React.FunctionComponent<RouteProps> = ({
  component: Component,
  ...routeProps
}) => {
  const { isSignedIn } = useAppContext();
  const ComponentToRender = Component as React.ElementType;
  return (
    <Route
      {...routeProps}
      render={(props) =>
        isSignedIn ? (
          <ComponentToRender {...props} />
        ) : (
          <Redirect to={ROUTES.LOGIN} />
        )
      }
    />
  );
};

export default PrivateRoute;

문제는 내가 소품에 설정된 구성 요소를 호출하고 싶지만 이것을 시도 할 때마다 Typescript에서 다음 오류가 발생한다는 것입니다.

JSX element type 'Component' does not have any construct or call signatures.  TS2604

오류 이미지

그 이유는 Route의 구성 요소 유형이 여기에 설명 된대로 Typescript가 예상하는 유형이 아니기 때문인 것 같습니다. https://github.com/microsoft/TypeScript/issues/28631, 따라서 방금 새로운 유형 (ComponentToRender)을 가진 복사본을 만들었습니다.

이것을 구현하는 더 좋은 방법이 있습니까? RouteProps 구성 요소를 덮어 쓸 수 있습니까?

감사!

답변

juanjo12x Aug 25 2020 at 16:18

마침내 오류를 이해하고 해결했습니다! 핵심은 구성 요소 속성과 렌더링 함수가 유형별로 다르게 처리된다는 것입니다. 를 사용함으로써 RoutePropsTypescript 컴파일러는 실제로 구성 요소 소품을 ComponentType (RouterProps에 따라 '구성 요소'에 지정된 유형)으로 설정하며 이는 index.d.ts 파일에서 볼 수 있듯이 렌더링 기능에 사용할 수 없습니다.

export interface RouteProps {
    location?: H.Location;
    component?: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
    render?: (props: RouteComponentProps<any>) => React.ReactNode;
    children?: ((props: RouteChildrenProps<any>) => React.ReactNode) | React.ReactNode;
    path?: string | string[];
    exact?: boolean;
    sensitive?: boolean;
    strict?: boolean;
}

이 동작은 PrivateRoute 구성 요소를 사용하지 않고 렌더링 함수를 Routes 파일로 이동하는 것만으로도 분명해집니다. 컴파일러가 유형을 올바르게 추론하기 때문에 아래 코드는 예상대로 작동합니다 (React.ReactNode).

<Route
  path={ROUTES.POINTS_HISTORY}
  render={(props) =>
    isSignedIn ? <PointsTransactions /> : <Redirect to={ROUTES.LOGIN} />
  }
></Route>

따라서 문제를 해결하기 위해 사용 사례에 필요한 매개 변수 만 사용하여 새 유형을 만듭니다.

import React from 'react';

import { useAppContext } from 'containers/App/AppContext';
import { RouteProps, Route, Redirect } from 'react-router-dom';
import { ROUTES } from 'utils/constants';

type PrivateRouteProps = {
  path: RouteProps['path'];
  component: React.ElementType;
};
const PrivateRoute: React.FunctionComponent<PrivateRouteProps> = ({
  component: Component,
  ...routeProps
}) => {
  const { isSignedIn } = useAppContext();
  return (
    <Route
      {...routeProps}
      render={(props) =>
        isSignedIn ? <Component /> : <Redirect to={ROUTES.LOGIN} />
      }
    />
  );
};

export default PrivateRoute;