当在 react 表单中使用 `onblur` 更新父组件级 `usestate` 时,整棵组件树会重新渲染,造成焦点丢失与交互延迟;将状态管理下放到子组件内部可避免此问题。
在你提供的代码中,gridLine 状态定义在 App 组件顶层,而 onBlur 处理函数 setHours 触发后会调用 setGridLine,导致整个 App 重新渲染。由于 TopBanner 是 App 的内联函数组件(非独立命名组件),每次 App 渲染都会创建一个全新的 TopBanner 函数实例 —— 这意味着其内部 DOM 节点被完全卸载并重建,包括你刚刚点击但尚未获得焦点的按钮。因此用户第一次点击时,事件目标尚在旧 DOM 树中;第二次点击才作用于新渲染的、已就绪的按钮节点,表现为“必须点两次”。
✅ 正确解法是:将状态逻辑下沉至实际使用它的 UI 组件层级,即把 useState 移入 TopBanner 内部:
function App() {
const TopBanner = () => {
// ✅ 状态属于 TopBanner 自身,仅触发局部重渲染
const [gridLine, setGridLine] = useState({ hours: "" });
const setHours = (event) => {
setGridLine(prev => ({ ...prev, hours: event.target.value }));
};
const addLine = (e) => {
e.preventDefault();
alert(`I am clicked, value = ${gridLine.hours}`);
};
return (
{/* 使用 value 而非 defaultValue,使其成为受控组件 */}
Add
);
};
return ;
}? 关键改进说明:
⚠️ 注意事项:

通过将状态锚定在最小必要作用域,即可彻底解决双击响应问题,同时提升应用性能与可维护性。
来电咨询