React中state多次更新完成后如何调用后续API函数
问题根因
React的状态更新是批量异步的,你在resetFn中连续调用三次setDataKey,React会把这三次更新合并成一次重渲染,你紧接着调用postFn时,拿到的还是更新前的旧data闭包,自然拿不到最新值。
最优方案:合并状态更新 + 给提交函数传参,避免依赖过时闭包
这个方案改动最小、性能最优,完全可以避开等待状态更新的逻辑,直接用拿到的接口返回值构造提交数据:
// 先改造postFn,支持传入待提交的数据,不传则用当前state const postFn = async (submitData = data) => { const response = await updateData({ body: submitData }); onSave({ response, widget, wrapperId, }); }; const resetFn = async () => { const defaultData = await getDefaultData({ query: { id: data.default_id }, }); // 合并三次更新为一次,减少重渲染次数 setData(d => ({ ...d, quantity: defaultData.quantity, name: defaultData.name, amount: defaultData.amount })); // 直接用已经拿到的最新数据构造提交内容,不用等state更新 const newData = { ...data, ...defaultData }; postFn(newData); };
方案二:通过useEffect监听状态 + 触发标识,确保状态更新后再提交
如果你的业务逻辑确实必须等状态更新到DOM之后再提交,可以加一个提交触发标识:
const [data, setData] = useState(() => getDataInit()); // 新增提交触发标识 const [shouldPost, setShouldPost] = useState(false); const postFn = async () => { const response = await updateData({ body: data }); onSave({ response, widget, wrapperId, }); }; // 监听提交标识和data,标识为true时才执行提交 useEffect(() => { if (shouldPost) { postFn(); // 提交完成重置标识 setShouldPost(false); } }, [shouldPost, data]); const resetFn = async () => { const defaultData = await getDefaultData({ query: { id: data.default_id }, }); setDataKey('quantity')(defaultData.quantity); setDataKey('name')(defaultData.name); setDataKey('amount')(defaultData.amount); // 标记需要提交,等三次状态合并更新完成后,useEffect会自动触发提交 setShouldPost(true); };
方案三:用useRef同步最新状态,随时拿最新值
如果不想加额外的状态和useEffect,也可以用ref同步最新的data值:
const [data, setData] = useState(() => getDataInit()); // 新增ref同步data const dataRef = useRef(data); // 每次data更新同步ref useEffect(() => { dataRef.current = data; }, [data]); const postFn = async () => { // 直接读ref的最新值,不会拿到过时闭包 const response = await updateData({ body: dataRef.current }); onSave({ response, widget, wrapperId, }); }; const resetFn = async () => { const defaultData = await getDefaultData({ query: { id: data.default_id }, }); setDataKey('quantity')(defaultData.quantity); setDataKey('name')(defaultData.name); setDataKey('amount')(defaultData.amount); // 等批处理完成再执行,setTimeout会在React状态更新、重渲染完成后执行 setTimeout(postFn, 0); };
内容的提问来源于stack exchange,提问作者Chandrika
相关产品推荐
相关产品推荐

