如何在react-router-v6路由上强制搜索参数的存在?


什么?

我正在构建一个React+Typescript应用程序,调用https://swapi.dev来渲染各种资源的页面,如people,filmsplanets

为什么?

我需要确保用户不能操纵URL,以便他们能够在不指定pageNumber参数的情况下转到/characters,如下所示:localhost:3000/character?page=1

如何?

作为一个例子,我有一个像下面这样的路由,我需要将用户重定向到/characters?page=1,如果他们输入enter/characters

<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route
path="characters"
element={<SwapiResourcePage resourceType={"people"}
/>
<Route
path="characters/:id"
element={<SwapiResourceDetailsPage resourceType={"people"} 
/>
</Route>
</Routes>

你试过什么?我已经尝试替换path=characters?page=:id的路径并将其设置为精确,但这在react-router中无效语法。我也快速浏览了一下https://reactrouter.com/docs/en/v6/,但我找不到任何相关的东西。

我能帮什么忙吗?

我需要帮助弄清楚如何在react-router路由上强制执行searchParams。

你不能在路由级别这样做,因为queryString参数不是路径的一部分。只有react-router-domRoute组件使用该路径

在匹配/路由的组件中使用useSearchParams钩子,检查pagequeryString参数,如果缺少,则发出一个命令式返回导航。

的例子:

import { useNavigate, useSearchParams } from 'react-router-dom';
const SwapiResourcePage = ({ resourceType }) => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const page = searchParams.get("page");
useEffect(() => {
if (!page) navigate(-1); // <-- no page param, navigate back
}, [navigate, page]);
...
if (!page) {
return null;
}
// render content
...

更新似乎你的问题更多的是提供一个备用的pagequeryString值,而不是防止访问路由的内容,如果page值丢失。您可以使用与上面相同的代码/逻辑,但不是使用navigate来发出路由的反向导航,只需更新queryString。

import { useSearchParams } from 'react-router-dom';
const SwapiResourcePage = ({ resourceType }) => {
const [searchParams, setSearchparams] = useSearchParams();
const page = searchParams.get("page");
useEffect(() => {
if (!page) {
// No page param, replace current
searchParams.set("page", 1);
setSearchparams(searchParams, { replace });
}
}, [searchParams, setSearchparams, page]);
...
if (!page) {
return null;
}
// render content
...

所以,我最终通过创建一个名为<ValidateQueryParams/>的中间组件来解决这个问题,下面是我为那些感兴趣的人使用的代码:

import React, { useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import validatePageNumber from "../helpers/validatePageNumber";
import SwapiResourcePage from "../pages/SwapiResourcePage";
interface ValidateQueryParamProps {
resourceType: string;
}
const ValidateQueryParam: React.FC<ValidateQueryParamProps> = ({
resourceType,
}) => {
const [searchParams, setSearchParams] = useSearchParams();
const pageNumber = searchParams.get("page");
useEffect(() => {
if (
pageNumber == null ||
!pageNumber.match(/d+/) ||
!validatePageNumber(resourceType, parseInt(pageNumber))
) {
setSearchParams({ page: "1" });
}
}, []);
return <SwapiResourcePage resourceType={resourceType} />;
};
export default ValidateQueryParam;
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route
path="people"
element={<ValidateQueryParam resourceType={"people"} />}
/>
<Route
path="people/:id"
element={<SwapiResourceDetailsPage resourceType={"people"} />}
/>
</Route>
</Routes>