应使用组件级 loading 局部状态统一控制按钮禁用与图标显隐,并用 try/finally 确保其必恢复,同时拦截重复点击。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

Codex生成的接口请求代码常因状态管理混乱导致按钮卡死、重复提交或loading图标不消失,根源在于没有统一驱动禁用态、图标态与请求态的单一状态变量。
用 loading 变量同步控制按钮禁用与图标显示
在组件内部定义一个布尔型 state:const [loading, setLoading] = useState(false)。
将按钮的 disabled 属性和图标显隐逻辑全部绑定到这个 loading 变量上,例如:disabled={loading}、{loading && <spinner></spinner>}。这一步必须做,否则三个状态会脱节——按钮灰了但圈还在转,或者圈停了按钮却点不动。
不要把 loading 状态提升到父组件或全局 store,【loading 必须是组件级局部状态】,否则 Codex 容易顺手改掉其他兄弟按钮的行为。
用 try/finally 保证 loading 状态必定恢复
在异步请求调用前后,必须将 setLoading(true) 放在请求发起前,setLoading(false) 放在 finally 块中。
错误写法:直接写在 await 后面——一旦请求抛出异常(如网络中断、401),setLoading(false) 就永远不会执行,按钮永久灰化。
正确结构:
setLoading(true);
try {
const data = await api.fetchList(filter);
setList(data);
} finally {
setLoading(false);
}
通过本地 Codex 或 OpenClaw OAuth 凭证直接调用 ChatGPT/Codex Responses 的 image_generation 工具来生成或编辑光栅图像,然后保存
拦截重复点击防止竞态请求
方法一:在点击事件开头加判断
if (loading) return;
方法二:用 useRef 缓存上一次请求的 AbortController,在新请求发起时 abort 旧请求。适用于列表筛选等高频切换场景。
注意:仅当 Codex 生成的代码已含 fetch 或 axios 调用时才适用此法;若它生成的是封装好的 hook,优先选方法一,改动最小、最可控。
验证 loading 行为是否符合预期
第一步:点击按钮,确认按钮立即变灰且转圈图标出现;
第二步:手动断网或 mock 接口延迟 2s,观察 loading 是否持续;
第三步:等待请求结束(无论成功或失败),确认按钮恢复可点击、转圈图标消失;
第四步:快速连点三次,检查只发出一个请求(浏览器 Network 面板验证)。如果发出多个,说明拦截逻辑没生效,需回退到方法一补上 if (loading) return;。










