Astro 多个 React 组件生成相同 id:检查关闭 streaming 的选项
开启 experimentalDisableStreaming 时,React integration 的非流式分支可能丢掉 identifierPrefix,造成重复 useId 与水合差异。恢复流式前先核对依赖兼容。
一个 Astro 页面里有多个 React 组件,它们的 useId() 都生成 _R_0_,label 或 ARIA 关系因此串到别的组件。若 React integration 配置了 experimentalDisableStreaming: true,先检查这个非流式分支。
具体组合是 Astro 7.4.2、@astrojs/react 7.1.1,静态输出也会遇到。重复 id 已经存在于服务端 HTML,不需要等浏览器交互才出现;带 client:idle 的 island 还可能在水合时得到另一份带前缀的 id。
独立 React root 需要各自的前缀
Astro 为页面中的 React root 生成不同前缀,服务端应把它作为 identifierPrefix 传给 React 渲染函数,客户端水合也要使用相同值。
7.1.1 的正式 server 实现中,流式分支传入包含前缀的 renderOptions,关闭 streaming 后却调用 renderToString(vnode),没有传入这个对象。各 root 都从相同的局部 id 序列开始,就可能在同页重复;客户端仍读取 island 上的 prefix,又产生服务端与客户端不一致。
如果重复 id 来自应用自己硬编码的 id="field",或者同一个组件内部重复使用了一条 id,这条配置调整并不能修复。先看源 HTML 与组件的 useId 使用位置,确认实际处于这个渲染分支。
能恢复 streaming 时,先撤去实验选项
若当初没有必须关闭流式渲染的依赖限制,可在现有配置中将该选项改回 false,或移除它,让 integration 使用正常流式路径:
react({ experimentalDisableStreaming: false })
这是现有 integrations 中 React 调用的局部配置,保留其他选项。重新构建静态页面,或按服务端输出方式重启对应应用,再检查实际 HTML;只重载浏览器不会改掉已生成的服务端标记。
如果该选项是为了兼容一个不支持 streaming 的组件库,不能仅为修 id 关闭其必要兼容措施。应核对包含前缀传递修正的正式 integration 版本,并回归原库。7.1.1 的正式实现仍缺少前缀传递,不能只升级 Astro 主包就假定 integration 跟着改变。
不要在输出 HTML 中批量替换重复 id,或用随机数代替 useId。label、aria-labelledby、aria-controls 与客户端水合都依赖同一关系,只改元素上的 id 会留下断开的引用。
同页唯一之外,还要验证关联正确
在同一页保留多个静态 React root 与需要水合的 island,检查 id 全部唯一、同一控件的 label 和 ARIA 引用准确,水合后 id 没有不匹配。再实际点击 label、操作弹层或折叠控件,确认焦点与控制对象正确。
若恢复 streaming 后原组件库出现行为变化,保留这组兼容冲突,恢复审定配置并选择正式修正路径。仅仅消除控制台告警,不足以验收辅助技术实际依赖的关联。


