React中等待state更新执行及useCallback调用报错问题求解
报错原因
1. useCallback 条件调用报错
React 要求所有 Hook 必须在组件最顶层作用域调用,不能出现在条件判断、循环、子函数内部、或组件 return 语句之后。检查你定义 useCallback 的代码位置,确认没有被嵌套在 if/else、for 等分支逻辑中,也没有写在组件返回 JSX 的代码之后。
2. 无效依赖项报错
首先存在拼写错误:状态更新函数为 setShowCustomMessage,对应的状态变量应为 showCustomMessage,你写的 showCustomerMessage 拼写不一致。
其次对 useCallback 的作用存在理解偏差:useCallback 是用来缓存函数实例的,仅当依赖项变化时才返回新的函数实例,并不会主动触发内部逻辑执行。你在依赖中传入的变量如果不是当前组件的 state、props 或其他被 React 追踪的响应式变量,就会触发外层作用域变量无效的报错。当前场景完全不需要使用 useCallback,属于不必要的封装。
等待状态更新完成的解决方案
React 中 useState 的更新是批量异步执行的,无法在调用 setter 后同步等待更新完成,可通过以下两种方式实现需求:
方案1:useEffect 监听状态变化(推荐)
将后续逻辑移到 useEffect 中,监听 showCustomMessage 的变化触发执行:
// 保存后续逻辑需要的参数 const [actionParams, setActionParams] = useState(null); const updateStatus = (r, e) => { setActionParams({r, e}); setShowCustomMessage(false); }; useEffect(() => { // 仅当showCustomMessage变为false且存在参数时执行后续逻辑 if (!showCustomMessage && actionParams) { setShowConfirmSave(true); // 此处为原有的axios相关逻辑,可从actionParams中取r、e参数 // 执行完成后可清空actionParams避免重复触发 setActionParams(null); } }, [showCustomMessage, actionParams]);
select 组件直接绑定 updateStatus 即可:
<select id="update_status" onChange={e => updateStatus(r, e)} value={r.status_id} />
方案2:flushSync 强制同步更新
如果需要立刻拿到更新后的状态,可以使用 react-dom 提供的 flushSync 方法强制同步刷新状态:
import { flushSync } from 'react-dom'; const updateStatus = (r, e) => { flushSync(() => { setShowCustomMessage(false); }); // 执行到此处时showCustomMessage已经完成更新 setShowConfirmSave(true); // 此处为axios相关逻辑 };
该方法会跳过 React 的批量更新机制直接刷新状态和DOM,非必要场景不推荐使用。
内容的提问来源于stack exchange,提问作者Paul Pickins

