React函数组件中多个state更新完成后如何调用指定函数
React 函数组件中,同步执行流里的setState调用默认是异步批处理的:你在mainfunction里连续调用三个状态更新方法时,React不会立刻修改state值、也不会立刻触发组件重渲染,而是会把这几个更新攒到当前同步任务执行完之后统一处理,所以直接跟在后面写的last_func执行时,拿到的还是更新前的旧状态,执行时机早于状态实际更新完成的节点。
顺带提一句,你贴的示例代码里第一个状态的setter命名有笔误:声明写的是const [variable, setvariable1] = useState(0),后面调用的却是setvariable(1),实际运行会报未定义错误,写代码时要注意setter名和声明保持一致。
方案1:useEffect 依赖监听(优先推荐,完全符合React设计规范)
把状态更新后要执行的逻辑放到useEffect回调中,把三个需要等待更新的state全部加入依赖数组。额外加一个ref标记位,避免组件首次挂载时就误触发last_func。
import { useState, useEffect, useRef } from 'react'; function Check(){ const [variable, setVariable] = useState(0); const [variable2, setVariable2] = useState(0); const [variable3, setVariable3] = useState(0); // 标记位:区分是否是主动触发的批量更新,避免首次挂载/其他更新场景误执行逻辑 const shouldRunLast = useRef(false); const last_func = () => { console.log("所有状态更新完成,执行对应逻辑"); // 此处可以拿到三个state更新后的值:1、1、1 } const mainfunction = () =>{ shouldRunLast.current = true; setVariable(1); setVariable2(1); setVariable3(1); } // 监听三个目标state的变化 useEffect(() => { // 满足标记位为真、且三个state都更新到目标值两个条件,才执行后续逻辑 if(shouldRunLast.current && variable === 1 && variable2 === 1 && variable3 === 1){ last_func(); // 执行完成后复位标记位,避免后续其他state更新误触发 shouldRunLast.current = false; } }, [variable, variable2, variable3]) return <button onClick={mainfunction}>触发更新</button> }
这个方案不受React版本批处理规则变动的影响,不管是React17及之前的事件回调批处理,还是React18之后全场景自动批处理,逻辑都能正常运行。
方案2:React18+ 用flushSync强制同步刷新更新(适合特殊场景)
如果你的项目React版本在18及以上,可以用react-dom提供的flushSync方法包裹所有状态更新调用,React会在回调执行完成后立刻强制刷新生效所有状态更新、完成组件重渲染,后续代码就能直接拿到更新后的状态。
注意:这个API会打破React的自动批处理优化,会额外增加重渲染开销,非必要不要滥用。
import { useState } from 'react'; import { flushSync } from 'react-dom'; function Check(){ const [variable, setVariable] = useState(0); const [variable2, setVariable2] = useState(0); const [variable3, setVariable3] = useState(0); const last_func = () => { console.log("所有状态更新完成,执行对应逻辑"); } const mainfunction = () =>{ flushSync(() => { setVariable(1); setVariable2(1); setVariable3(1); }) // 代码执行到此处时,上面三个state已经全部更新完成,组件重渲染也已结束 last_func(); } return <button onClick={mainfunction}>触发更新</button> }
方案3:无状态依赖场景不需要硬等更新完成
如果你的last_func根本不需要读取这三个state的最新渲染值,只是逻辑上要等三个更新请求都发起之后再执行,那完全不需要额外处理——setState本身只是向React提交一个更新请求,你在同步代码里按顺序写完三个setState,再调用last_func,就已经满足了“提交完所有更新再执行后续逻辑”的要求,不需要硬等React把state修改完、重渲染完。这种场景下只要注意不要在last_func里直接读取三个state的最新值就行,需要用到新值直接把1作为参数传入last_func使用即可。
注意:不要把类组件
setState支持第二个完成回调的写法硬套到函数组件上,函数组件的useState返回的setter不支持传入更新完成回调,硬写不会生效。
内容的提问来源于stack exchange,提问作者Srinu Kesari

