我是Typescript的新手,我正在将我们的应用程序的react路由器版本从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 };
我知道渲染方法和重定向组件被废弃了,这就是我到目前为止所做的(WIP):
//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("/");
});
});
我认为你把路由保护复杂化了一点。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
prop,因此嵌套路由的路径是用于匹配/渲染的。
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>
);
};