reactjs - React/TypeScript 条件付きルーティング - React Router Dom v5 から v6 への更新
私はTypeScriptの初心者で、アプリのReact Routerのバージョンをv5からv6に更新しています。この条件付きルーティングのラッパーをどのように更新するかでつまずいています:
// v5
import React from "react";
import {
Redirect,
RedirectProps,
Route,
RouteComponentProps,
RouteProps,
} from "react-router-dom";
export type ConditionalRouteProps = React.FC<
RouteProps &
Partial<RedirectProps> & {
condition?: boolean;
}
>;
const ConditionalRoute: ConditionalRouteProps = ({
component: Component,
condition = false,
children,
...rest
}) => {
const toRender = (props: RouteComponentProps): React.ReactElement | null => {
if (!condition) return <Redirect to={REDIRECT_DESTINATION} />;
if (Component) return <Component {...props} />;
return <>{children}</>;
};
return <Route {...rest} render={toRender} />;
};
export { ConditionalRoute };
レンダーメソッドとRedirectコンポーネントが廃止されたことは知っています。これまでに私が行ったことは次の通りです(作業中):
//v6
import React from "react";
import { Navigate, NavigateProps, Route, RouteProps } from "react-router-dom";
export type ConditionalRouteProps = React.FC<
RouteProps &
Partial<NavigateProps> & {
condition?: boolean;
}
>;
const ConditionalRoute: ConditionalRouteProps = ({
element: Element,
condition = false,
children,
...rest
}) => {
return (
<Route {...rest}>
{!condition ? (
<Navigate to={REDIRECT_DESTINATION} />
) : Element ? (
<Element />
) : (
<>{children}</>
)}
</Route>
);
};
export { ConditionalRoute };
そして、<Element /> で私が得ているエラーは次の通りです:JSX 要素型 'Element' にはコンストラクトまたは呼び出しのシグネチャが存在しません。ts(2604)
これはラッパーが呼び出されている方法です:
const AdminRoute: ConditionalRouteProps = (props) => {
const { isAdmin } = useContext(UserContext);
return <ConditionalRoute condition={isAdmin} {...props} />;
};
...
const TestPage: React.FC = () => {
return (
<>
<Link to="/admin" data-testid="navigateToAdmin">
Navigate to admin
</Link>
</>
);
};
const TestRoutes: React.FC = () => {
return (
<Routes>
<Route
path="/admin"
element={<AdminRoute element={() => <h1>Admin Page</h1>} />}
/>
<Route path="/" element={<TestPage />} />
</Routes>
);
};
describe("<AdminRoute />", () => {
it("should let admin users in", () => {
const { history } = renderWithProviders(<TestRoutes />, {
providerProps: { userProviderProps: { isAdmin: true } },
});
userEvent.click(screen.getByTestId("navigateToAdmin"));
expect(history.location.pathname).toEqual("/admin");
});
it("should redirect everyone else", () => {
const { history } = renderWithProviders(<TestRoutes />);
userEvent.click(screen.getByTestId("navigateToAdmin"));
expect(screen.queryByText("Admin Page")).not.toBeInTheDocument();
expect(history.location.pathname).toEqual("/");
});
});
回答 #1
私はあなたがルート保護を少し複雑にしていると思います。react-router-dom@6 のルートレンダリングはv6ではかなり異なります。私たちは自分のコンポーネント内で直接 Route をレンダリングするのではなく、Routes コンポーネントにレンダリングするか、他の Route コンポーネントの下に直接ネストしてルートネストを行う必要があります。
私は次の実装を提案します。
//v6
import React from "react";
import { Navigate, Outlet } from "react-router-dom";
interface IConditionalRoute = {
condition: boolean;
};
const ConditionalRoute = ({ condition = false }: IConditionalRoute) => {
return condition ? <Outlet /> : <Navigate to={REDIRECT_DESTINATION} />;
};
export { ConditionalRoute };
注意:私はTypeScriptにあまり強くないので、構文が完全に正しいとは限りませんが、このインターフェース/コードは必要なものに近づけるはずです。
使用法:
AdminRoute は、レイアウトルートと呼ばれるルートでレンダリングされます。このルートは Outlet をレンダリングし、他の Route コンポーネントをラップします。入れ子になったルートのパスがマッチング/レンダリングに使用されるように、path プロパティは省略してください。
const AdminRoute: ConditionalRouteProps = () => {
const { isAdmin } = useContext(UserContext);
return <ConditionalRoute condition={isAdmin} />;
};
const TestRoutes: React.FC = () => {
return (
<Routes>
<Route element={<AdminRoute />}>
<Route path="/admin" element={<h1>Admin Page</h1>} />
</Route>
<Route path="/" element={<TestPage />} />
</Routes>
);
};
