ผ่านอุปกรณ์ประกอบฉากในเส้นทางส่วนตัวด้วย typescript และตอบสนอง
ฉันกำลังใช้งาน Authenticated Routes กับ typescript และ React โดยใช้ render props ของส่วนประกอบ Route จาก React Router v4
เส้นทาง:
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
รูปภาพของข้อผิดพลาด
สาเหตุน่าจะเป็นเพราะประเภทส่วนประกอบของเส้นทางไม่ใช่สิ่งที่ typescript คาดหวังตามที่อธิบายไว้ที่นี่: https://github.com/microsoft/TypeScript/issues/28631ดังนั้นฉันจึงเพิ่งสร้างสำเนาซึ่งมีประเภทใหม่ (ComponentToRender)
มีวิธีที่ดีกว่านี้ในการนำไปใช้หรือไม่? อาจจะเขียนทับองค์ประกอบของ RouteProps?
ขอบคุณ!
คำตอบ
ในที่สุดฉันก็เข้าใจข้อผิดพลาดและแก้ไขได้! กุญแจสำคัญคือแอตทริบิวต์องค์ประกอบและฟังก์ชันการแสดงผลได้รับการจัดการประเภทที่แตกต่างกัน โดยการใช้RouteProps
คอมไพเลอร์ typescript กำลังตั้งค่าอุปกรณ์ประกอบฉากเป็น 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;
}
พฤติกรรมนี้จะปรากฏชัดเจนเพียงแค่ย้ายฟังก์ชันการแสดงผลไปยังไฟล์ Routes และไม่ใช้คอมโพเนนต์ PrivateRoute โค้ดด้านล่างทำงานตามที่คาดไว้เนื่องจากคอมไพเลอร์ระบุประเภทได้ถูกต้อง (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;