React useState更新dataLoaded状态仅页面重载生效问题
问题原因
- React 函数组件中
useState触发的状态更新属于异步批处理逻辑,调用状态更新方法后,当前函数执行上下文捕获的是本次渲染周期的闭包值,不会立刻同步变更,只有组件完成下一次重渲染后,才能在新的执行上下文中拿到最新的状态值。 - 你在同一个
fetchData作用域内调用setDataLoaded(true)后,立刻读取dataLoaded做判断,拿到的永远是闭包缓存的初始值false,因此getSeatsArrangement不会被触发。同理你在setShowTimes、setShowdate之后立刻打印这两个状态,也拿不到最新值,不是状态更新失败,是当前作用域访问不到更新后的值。 - 额外代码问题:你定义的
requestOptions函数声明了token入参但从未传入、使用,属于冗余代码;座位查询接口中showtimeId和movieDate被硬编码为固定值,就算方法触发也拿不到和当前场次匹配的座位数据。
修复方案
优先选择第一种方案,逻辑更直观,也避免不必要的重渲染监听:
- 方案1:去掉无意义的状态判断,接口返回数据后用局部变量直接传参调用后续方法,不依赖闭包中的状态
修正后的核心代码如下:
const [dataLoaded, setDataLoaded] = useState(false) useEffect(() =>{ const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/movie/${path.eventId}/venue/${path.venue}/showtime`; // 去掉冗余的token入参 const requestOptions = { method: 'GET', headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' } }; const fetchData = async () => { try { const response = await fetch(url, requestOptions); const json = await response.json(); // 先把接口返回值存在局部变量里 const currentShowTimes = json.data.dateShowtimes[0].showtimes[0]; const currentShowDate = json.data.dateShowtimes[0].date; // 同步更新页面状态 setShowTimes(currentShowTimes); setShowdate(currentShowDate); setDataLoaded(true); // 直接用局部变量传参调用,不需要等状态更新完成 getSeatsArrangement(currentShowTimes.showtimeId, currentShowDate); } catch (error) { console.log("error",error); } }; fetchData(); },[]); // 修改方法为接收参数,不从外层闭包读状态 const getSeatsArrangement = async (showtimeId, movieDate) => { const requestOptions = { method: 'GET', headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' } }; try{ // 替换硬编码的参数为传入值 const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/seat?venueId=${path.venue}&movieId=${path.eventId}&showtimeId=${showtimeId}&movieDate=${movieDate}`; const response = await fetch(url,requestOptions); const json = await response.json(); setReservedSeats(json.data.reservedSeats.reservedSeat) setNonReservedSeats(json.data.reservedSeats.nonReservedSeats) } catch(error) { console.log("error",error); } }
- 方案2:如果业务逻辑必须依赖状态变更触发,将后续执行逻辑放到独立的
useEffect中,监听依赖的状态,等状态更新完成、组件重渲染后自动执行
只需要在原有代码基础上新增如下监听逻辑即可:
// 监听依赖的状态,状态更新完成后自动执行 useEffect(() => { if(dataLoaded && showTimes?.showtimeId && showdate) { getSeatsArrangement(); } }, [dataLoaded, showTimes, showdate])
注意使用该方案时,也要记得修正getSeatsArrangement里硬编码接口参数的问题,否则拿到的座位数据和当前场次不匹配。
内容的提问来源于stack exchange,提问作者Ryan Fonseka
相关产品推荐
相关产品推荐

