React Router 表单提交后 URL 出现 ?index 参数是怎么回事?
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
在 React Router 应用中提交表单后,你可能会发现地址栏里多出一个来路不明的?index,例如原本停留在/projects,提交之后变成了/projects?index。这不是 bug:在嵌套路由下,父路由和它的 index 路由共享同一个 URL,?index参数用来区分这次表单提交到底应该调用哪一个路由的action。理解它的触发条件后,你可以判断自己应用里出现的?index是预期行为,还是表单放错了位置。
为什么需要 ?index:一个 URL 对应两个 action
先看一段典型的路由配置(见 index-query-param 说明):
import { type RouteConfig, route, index, } from "@react-router/dev/routes"; export default [ route("projects", "./pages/projects.tsx", [ index("./pages/index.tsx"), route(":id", "./pages/project.tsx"), ]), ] satisfies RouteConfig;这个配置创建了两个都能匹配/projects的路由:
- 父路由
./pages/projects.tsx(布局) - index 路由
./pages/index.tsx
index 路由会渲染在父路由的<Outlet>里、位于父路由的 URL 上(见 Routing 文档),它无法通过路径段来区分自己。导航时这不是问题——所有匹配路由的loader都会被调用以拼出 UI;但表单提交时只会调用一个 action。因此?index参数的作用就是消除歧义:
- 带
?index的 action 提交给 index 路由 - 不带
?index的 action 提交给父路由
验证:?index 由哪个路由的表单触发
判断自己应用里的?index是哪来的,可以直接看<Form>的action和它所在的组件位置。
显式指定 action 时
两种写法的目标路由完全不同:
<Form method="post" action="/projects" /> <Form method="post" action="/projects?index" />第一条提交到父路由的 action,第二条提交到 index 路由的 action。你可以在两个路由里分别定义action,提交后观察哪一个被执行,确认路由归属是否符合预期。
未指定 action 时:?index 会被自动追加
<Form>的action省略时默认指向“上下文中最近的路由”(见 Form 组件文档)。文档明确说明:当<Form>渲染在 index 路由中且没有action时,?index会被自动追加,确保表单提交到 index 路由:
function ProjectsIndex() { return <Form method="post" />; }这个表单提交后实际会 POST 到/projects?index,因为它渲染在projectsindex 路由的上下文中。如果把同样的代码挪到父布局./pages/projects.tsx里,它就改为 POST 到/projects(不带?index)。
排查时按这个顺序核对:
- 出问题的表单渲染在哪个路由模块里;
- 它有没有写
action; - 所在路由是 index 路由还是布局路由。
如果表单本意是提交给父路由,但组件被放在了 index 路由里且没写action,提交后 URL 出现?index就是这条规则生效的表现,把表单移到父布局或显式写出action="/projects"即可。
同样的规则适用于所有“表亲”提交方式(文档中给出的示例):
function Component() { const submit = useSubmit(); submit({}, { action: "/projects" }); submit({}, { action: "/projects?index" }); }function Component() { const fetcher = useFetcher(); fetcher.submit({}, { action: "/projects" }); fetcher.submit({}, { action: "/projects?index" }); <fetcher.Form action="/projects" />; <fetcher.Form action="/projects?index" />; <fetcher.Form />; // defaults to the route in context }<fetcher.Form />不写action时同样“默认提交给上下文中当前路由”,index 路由里会自动带上?index。另外,useFormAction()返回的也是“上下文中最近路由”的 URL,这是<Form>解析省略action时内部使用的机制(见 useFormAction 文档)。
服务端看到的 URL 长什么样
排查 action 时你可能会注意到:?index只在浏览器地址栏和提交请求中可见,默认情况下并不会以原样传进你的loader、action或middleware。根据 v7 升级文档,React Router 默认会规范化传给这些函数的request.url,移除.data后缀和?index、?_routes这类内部搜索参数。
也就是说,如果你在 index 路由的action里打印new URL(request.url).search,看到的可能已经是去掉?index的结果,这是默认行为,不代表参数没有生效。v7 升级文档还给出了future.v8_passThroughRequests标志,开启后会取消上述规范化,把原始 HTTPrequest直接交给 handler——如果你需要基于.data后缀或搜索参数区分文档请求与数据请求,可以参考该文档中的配置:
import type { Config } from "@react-router/dev/config"; export default { future: { v8_passThroughRequests: true, }, } satisfies Config;小结
?index不是残留脏数据,而是 React Router 区分“提交给父路由还是 index 路由”的机制,因为它无法靠路径段区分共享同一 URL 的两个路由;- 表单渲染在 index 路由中且未写
action时,?index会自动追加;渲染在布局路由中则不会; - 需要精确控制提交目标时,显式写出带或不带
?index的action; - 默认情况下服务端 handler 收到的
request.url已移除?index,排查时不要以 action 里打印不出该参数作为判断依据。
主要参考:Index Query Param 说明、Form 组件 API、Routing(Index Routes 一节)、v7 升级文档。
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考