React useState状态未按预期更新:刷新按钮点击后Toast不触发
问题原因
React的useState状态更新是异步批量处理的,当你在refreshClicked里调用setRefreshBtnClicked(true)后立刻执行fetchAnalytics,此时fetchAnalytics函数捕获的是当前渲染周期里的refreshBtnClicked值(也就是初始的false)——因为状态更新还没完成,组件还没重新渲染,闭包里的变量不会同步更新。
解决方案
这里提供三种可行的修改方式:
方案1:直接传递参数(最简洁)
不需要依赖状态,把是否显示Toast作为参数传给fetchAnalytics:
const [refreshBtnClicked, setRefreshBtnClicked] = React.useState(false); const refreshClicked = () => { // 直接传标记位给fetchAnalytics fetchAnalytics(true); } const fetchAnalytics = async (showToast = false) => { setLoading(true); try{ let analyticsResponse = await Axios({ method: 'post', url: process.env.REACT_APP_BEATS_GENERAL_REPORT, headers: { "Access-Control-Allow-Origin": "*", Authorization: "Bearer " + sessionStorage.getItem("idToken") }, data: formData }) setAnalyticsData(analyticsResponse.data); setShowCards(true); // 根据传入的参数判断是否显示Toast if (showToast) { ToastSuccess('Updated successfully'); } setLoading(false); }catch(err){ console.log(err); ToastError('Error while fetching data'); setLoading(false); } }
方案2:使用useRef保存状态(适合无需触发渲染的场景)
useRef的更新是同步的,不会触发组件重新渲染,能在函数里拿到最新值:
// 用ref代替useState保存标记 const refreshBtnClickedRef = React.useRef(false); const refreshClicked = () => { refreshBtnClickedRef.current = true; fetchAnalytics(); } const fetchAnalytics = async () => { setLoading(true); try{ let analyticsResponse = await Axios({ method: 'post', url: process.env.REACT_APP_BEATS_GENERAL_REPORT, headers: { "Access-Control-Allow-Origin": "*", Authorization: "Bearer " + sessionStorage.getItem("idToken") }, data: formData }) setAnalyticsData(analyticsResponse.data); setShowCards(true); // 读取ref的最新值 if (refreshBtnClickedRef.current) { ToastSuccess('Updated successfully'); refreshBtnClickedRef.current = false; } setLoading(false); }catch(err){ console.log(err); ToastError('Error while fetching data'); refreshBtnClickedRef.current = false; setLoading(false); } }
方案3:用useEffect监听状态变化(符合React状态驱动逻辑)
让状态更新触发useEffect,再调用fetchAnalytics,确保执行时状态已经更新:
const [refreshBtnClicked, setRefreshBtnClicked] = React.useState(false); const refreshClicked = () => { setRefreshBtnClicked(true); } // 监听refreshBtnClicked变化,为true时调用fetchAnalytics React.useEffect(() => { if (refreshBtnClicked) { fetchAnalytics(); } }, [refreshBtnClicked]); const fetchAnalytics = async () => { setLoading(true); try{ let analyticsResponse = await Axios({ method: 'post', url: process.env.REACT_APP_BEATS_GENERAL_REPORT, headers: { "Access-Control-Allow-Origin": "*", Authorization: "Bearer " + sessionStorage.getItem("idToken") }, data: formData }) setAnalyticsData(analyticsResponse.data); setShowCards(true); ToastSuccess('Updated successfully'); setRefreshBtnClicked(false); setLoading(false); }catch(err){ console.log(err); ToastError('Error while fetching data'); setRefreshBtnClicked(false); setLoading(false); } }
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

