Lulus props komponen di Private Route dengan Typecript dan React
Saya mengimplementasikan Routes yang Diautentikasi dengan Typecript dan React menggunakan alat peraga render dari komponen Route dari React Router v4.
Rute:
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;
Rute Pribadi:
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;
Masalahnya adalah bahwa saya ingin memanggil set Komponen pada props, namun, setiap kali saya mencoba ini, Typecript memunculkan kesalahan berikut.
JSX element type 'Component' does not have any construct or call signatures. TS2604
Gambar kesalahan
Alasannya sepertinya karena tipe komponen untuk Route bukanlah yang diharapkan Typecript seperti yang dijelaskan di sini: https://github.com/microsoft/TypeScript/issues/28631, oleh karena itu saya baru saja membuat salinan yang memiliki tipe baru (ComponentToRender).
Apakah ada cara yang lebih baik untuk menerapkan ini? Mungkin menimpa elemen komponen RouteProps?
Terima kasih!
Jawaban
Saya akhirnya mengerti kesalahan dan menyelesaikannya! Kuncinya adalah atribut komponen dan fungsi render ditangani berdasarkan tipe yang berbeda. Dengan menggunakan RouteProps
, compiler Typecript sebenarnya mengatur props komponen sebagai ComponentType (tipe yang ditentukan untuk 'komponen' menurut RouterProps), yang tidak dapat digunakan untuk fungsi render, seperti yang terlihat di file 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;
}
Perilaku ini menjadi jelas hanya dengan memindahkan fungsi render ke dalam file Routes dan tidak menggunakan komponen PrivateRoute. Kode di bawah ini berfungsi seperti yang diharapkan, karena kompiler menyimpulkan jenisnya dengan benar (React.ReactNode).
<Route
path={ROUTES.POINTS_HISTORY}
render={(props) =>
isSignedIn ? <PointsTransactions /> : <Redirect to={ROUTES.LOGIN} />
}
></Route>
Oleh karena itu, untuk mengatasi masalah tersebut, saya hanya membuat tipe baru dengan hanya parameter yang diperlukan untuk kasus penggunaan saya.
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;