
在 React 18.2.0 中,当使用 {...{}} 展开空对象并将其置于 key 之前时,可能导致 Each child in a list should have a unique 'key' prop 警告误报;而将 key 显式前置则可避免——这并非 React 的设计缺陷,而是 JSX 编译与属性合并机制共同作用的结果。
在 react 18.2.0 中,当使用 `{...{}}` 展开空对象并将其置于 `key` 之前时,可能导致 `each child in a list should have a unique 'key' prop` 警告误报;而将 `key` 显式前置则可避免——这并非 react 的设计缺陷,而是 jsx 编译与属性合并机制共同作用的结果。
该问题的本质不在于 key 值是否唯一("formKey" 本身是合法字符串),而在于 JSX 属性的解析顺序与 key 的归属判定逻辑。
React 要求 key 必须直接出现在 JSX 元素的顶层属性中,且不能被对象展开(spread)覆盖或“隐藏”。当写成:
<div {...{}} key={"formKey"}>
<input type="text" />
<button>Test</button>
</div>JSX 编译器(如 Babel)会先处理 {...{}},生成一个空对象作为 props,再将 key 作为独立属性追加。但在某些版本的 React(尤其是 v18.2.0 的特定构建或开发模式下),若存在父级为数组/列表上下文(例如 map 渲染多个同级 <div>),React 的 reconciler 可能因属性合并时机问题,未能正确识别 key 已被显式声明,转而将整个 <div> 视为无 key 的子节点,从而触发警告。
而改为:
<div key={"formKey"} {...{}}>
<input type="text" />
<button>Test</button>
</div>key 被明确置于展开运算符之前,确保其作为第一个静态属性被解析并注入 props.key,React 能稳定识别该 key,避免误判。
⚠️ 注意事项:
- key 必须是静态、稳定、唯一(在同级兄弟中)的字符串或数字,不可使用 Math.random() 或索引 index(除非数据绝对静态且永不增删);
- 展开对象(如 {...props})不应覆盖或干扰 key —— 始终将 key 放在展开运算符之前;
- 此现象多见于开发环境(react.development.js),生产构建中通常被优化忽略,但绝不应依赖此行为;
- 若你实际在 map 中渲染多个 <div>,请确保每个 key 唯一,例如:key={item.id},而非全部硬编码为 "formKey"。
✅ 正确实践示例:
function FormList({ forms }) {
return (
<div>
{forms.map((form) => (
// ✅ key 显式前置,且基于数据唯一标识
<div key={form.id} {...form.extraProps}>
<input type="text" value={form.title} />
<button onClick={() => handleSubmit(form.id)}>Submit</button>
</div>
))}
</div>
);
}总结:key 的位置不是语法糖,而是 React 协调算法的关键信号。将 key 置于展开属性之前,是保证其被正确识别的强制约定——这不是“巧合”,而是 JSX 属性合并规则与 React 内部 ReactElement 构造逻辑共同决定的最佳实践。


















