Typecript ve React ile Private Route içinde bileşen proplarını geçir
React Router v4'ten Route bileşeninin render props'larını kullanarak Typescript ve React ile Authenticated Routes uyguluyorum.
Rotalar:
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;
Özel Rota:
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;
Sorun şu ki, proplarda Bileşen setini çağırmak istiyorum, ancak bunu her denediğimde Typescript aşağıdaki hatayı veriyor.
JSX element type 'Component' does not have any construct or call signatures. TS2604
Hatanın görüntüsü
Bunun nedeni, Yol için bileşen türünün, burada açıklandığı gibi Typecript'in beklediği gibi olmamasıdır: https://github.com/microsoft/TypeScript/issues/28631, bu nedenle yeni bir türe (ComponentToRender) sahip bir kopya oluşturdum.
Bunu uygulamanın daha iyi bir yolu var mı? Belki RouteProps bileşen öğesinin üzerine yazıyorsunuz?
Teşekkürler!
Yanıtlar
Sonunda hatayı anladım ve çözdüm! Önemli olan, bileşen özniteliği ve oluşturma işlevinin tür bazında farklı şekilde ele alınmasıydı. Kullanarak RouteProps
, Typescript derleyicisi aslında bileşen props'larını, index.d.ts dosyasında göründüğü gibi render işlevi için kullanılamayan ComponentType (RouterProps'a göre 'component' için belirtilen tip) olarak ayarlıyor.
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;
}
Bu davranış, yalnızca render işlevini Routes dosyasına taşıyarak ve PrivateRoute bileşenini kullanmadan belirgin hale gelir. Aşağıdaki kod beklendiği gibi çalışır, çünkü derleyici türü doğru bir şekilde belirler (React.ReactNode).
<Route
path={ROUTES.POINTS_HISTORY}
render={(props) =>
isSignedIn ? <PointsTransactions /> : <Redirect to={ROUTES.LOGIN} />
}
></Route>
Bu nedenle, sorunu çözmek için, yalnızca kullanım durumum için gerekli parametreleri içeren yeni bir tür oluşturuyorum.
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;