
react 中的 `
在你提供的代码中,<Select> 组件仅通过 onChange 更新状态,但未将 difficulty 状态同步回传给 value 属性,导致其实际处于非受控(uncontrolled)状态。React 会因此忽略后续的状态变化,表现为:点击选项后视觉无反馈、需重复点击、切换焦点后恢复 placeholder —— 这正是典型的“半受控”陷阱。
✅ 正确做法是让 <Select> 成为完全受控组件:显式声明 value 并确保其始终与 useState 的当前值一致:
import { EnumDifficulty } from "@/components/Utils/EnumDifficulty";
import { useState } from "react";
const [difficulty, setDifficulty] = useState<string | undefined>(undefined);
return (
<FormControl isRequired>
<FormLabel>Niveau</FormLabel>
<Select
placeholder="Sélectionner le niveau"
value={difficulty || ""} // ✅ 关键修复:绑定 value,空值时设为 "" 以兼容 placeholder
onChange={(e) => setDifficulty(e.target.value || undefined)}
>
{EnumDifficulty.map((diff) => (
<option key={diff} value={diff}> {/* ✅ 建议用 diff 作 key 和 value,避免 uuidv4() 导致重渲染问题 */}
{diff}
</option>
))}
</Select>
</FormControl>
);? 关键优化说明:
- value={difficulty || ""}:当 difficulty 为 undefined 时,传入空字符串 "",使 placeholder 能正常显示;若传 undefined,Chakra UI 的 <Select> 可能降级为非受控行为。
- value={diff}(而非省略):每个 <option> 必须显式设置 value 属性,否则 e.target.value 将取文本内容(虽此处一致),但语义不明确且易出错。
- key={diff}:使用稳定、唯一、可预测的 diff 作为 key,替代 uuidv4() —— 后者每次渲染都生成新 key,破坏 React 列表复用机制,引发不必要的重渲染和状态丢失。
⚠️ 额外注意事项:
- 若初始值允许为空,请确保 useState 初始化为 undefined 或 "",并与 value 的处理逻辑一致;
- 不要混用受控与非受控模式(例如:有时传 value,有时不传),React 会抛出警告并导致不可预测行为;
- 使用 Chakra UI 时,确认 <Select> 版本支持受控模式(v2+ 完全支持,v1 需检查文档)。
遵循以上实践,你的 Select 组件即可实现:单次点击即时生效、选中态持久保持、失焦不重置、响应式更新——真正稳定可靠。










