React异步调用中更新State后无法获取最新值的问题求助
问题原因与解决方案
问题原因
React的状态更新是异步的,而且函数组件里的状态值在当前渲染周期的闭包中是固定的。你在fetchAllAPICalls里先调用fetchAllSelections,虽然执行了setIsSelected(selected),但这个状态更新不会立刻生效——组件要等当前渲染周期结束后才会重新渲染、更新isSelected的值。而fetchAllQuotes是紧接着执行的,它捕获的是组件初始渲染时的isSelected初始值(也就是false),所以哪怕第一个API返回了选中项,这里拿到的还是旧值。
解决方案一:直接传递计算结果(最推荐)
既然你在fetchAllSelections里已经算出了selected的布尔值,没必要依赖状态,直接把这个值传给fetchAllQuotes就行:
const [isOptionEnabled, setIsOptionEnabled] = useState<boolean>(false); const [isSelected, setIsSelected] = useState<boolean>(false); useEffect(() => { fetchAllAPICalls(); }, []); const fetchAllAPICalls = async () => { const selected = await fetchAllSelections(); await fetchAllQuotes(selected); }; const fetchAllSelections = async () => { const url = getUrlForSelections(); const params = getParamsForSelections(); let selected = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { if(item.isSelected) selected = true; }); setIsSelected(selected); return selected; // 返回计算好的selected值 } const fetchAllQuotes = async (selected: boolean) => { // 接收参数 const url = getUrlForQuotes(); const params = getParamsForQuotes(); let isPresent = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { // doSomething and set isPresent = true; }); setIsOptionEnabled(isPresent && selected); // 用传递的参数而非状态 }
解决方案二:用useEffect监听状态变化
如果必须依赖状态,可以在isSelected更新后触发fetchAllQuotes,同时控制执行时机避免重复调用:
const [isOptionEnabled, setIsOptionEnabled] = useState<boolean>(false); const [isSelected, setIsSelected] = useState<boolean>(false); const [hasFetchedSelections, setHasFetchedSelections] = useState(false); useEffect(() => { fetchAllSelections(); }, []); // 当isSelected更新且已获取过选择项时,调用fetchAllQuotes useEffect(() => { if(hasFetchedSelections) { fetchAllQuotes(); } }, [isSelected, hasFetchedSelections]); const fetchAllSelections = async () => { const url = getUrlForSelections(); const params = getParamsForSelections(); let selected = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { if(item.isSelected) selected = true; }); setIsSelected(selected); setHasFetchedSelections(true); // 标记已完成选择项获取 } const fetchAllQuotes = async () => { const url = getUrlForQuotes(); const params = getParamsForQuotes(); let isPresent = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { // doSomething and set isPresent = true; }); setIsOptionEnabled(isPresent && isSelected); // 此时isSelected已是更新后的值 }
解决方案三:使用useRef保存最新状态
用useRef存储最新的isSelected值,这样在异步函数里能直接访问到最新状态:
const [isOptionEnabled, setIsOptionEnabled] = useState<boolean>(false); const [isSelected, setIsSelected] = useState<boolean>(false); const isSelectedRef = useRef<boolean>(false); // 创建ref保存最新状态 // 同步ref与状态值 useEffect(() => { isSelectedRef.current = isSelected; }, [isSelected]); useEffect(() => { fetchAllAPICalls(); }, []); const fetchAllAPICalls = async () => { await fetchAllSelections(); await fetchAllQuotes(); }; const fetchAllSelections = async () => { const url = getUrlForSelections(); const params = getParamsForSelections(); let selected = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { if(item.isSelected) selected = true; }); setIsSelected(selected); } const fetchAllQuotes = async () => { const url = getUrlForQuotes(); const params = getParamsForQuotes(); let isPresent = false; const res = await AXIOS_GET(url, params); res.data.forEach((item) => { // doSomething and set isPresent = true; }); setIsOptionEnabled(isPresent && isSelectedRef.current); // 用ref的最新值 }
内容的提问来源于stack exchange,提问作者Aftab
相关产品推荐
相关产品推荐

