React中如何实现异步调用等待另一异步调用完成?——确保stateUserInfo填充后再设置stateToken触发HomeScreen条件渲染
你的问题核心在于setState是异步操作,而且你用了不太靠谱的setTimeout来延迟更新——这不仅没法保证state真的更新完成,还会让用户平白等不必要的时间。另外你当前的逻辑是先延迟设置stateUserInfo,然后立刻就设置stateToken,这必然会导致stateToken先触发HomeScreen渲染,但stateUserInfo还没准备好。
下面分两种常见的React组件类型给出解决方案:
情况1:类组件(Class Component)
类组件的setState方法支持第二个参数——一个回调函数,这个函数会在state更新完成、组件重新渲染后执行。我们可以利用这个特性,把setStateToken放在这个回调里:
async function getUserDetails(jwt) { const data = await axios.get(apiValidate + '&JWT=' + jwt); // 去掉多余的setTimeout,直接调用setState,并在回调中设置token this.setState({ userInfo: data.data }, () => { // 这个回调会在stateUserInfo完全更新后执行 this.setState({ token: jwt }); }); }
这样就能确保只有当stateUserInfo更新完成后,才会设置stateToken,触发HomeScreen条件渲染时用户信息已经准备完毕。
情况2:函数组件(Function Component + useState)
函数组件中,useState的setter没有回调参数,但我们可以用useEffect钩子监听stateUserInfo的变化,当它从初始值变为有效数据时,再设置stateToken。
先确保你已经声明了对应的state和ref(用来保存jwt,避免作用域问题):
import { useState, useEffect, useRef } from 'react'; // ...其他组件代码 const [stateUserInfo, setStateUserInfo] = useState(null); const [stateToken, setStateToken] = useState(null); const jwtRef = useRef(null); // 用ref保存jwt,确保useEffect能拿到最新值
然后修改submitLogin和getUserDetails函数,并添加useEffect:
async function submitLogin() { try { const data = await axios.post(apiAuth, {username: email, password: password, auth: auth}); const jwt = data.data.data.jwt; console.log(jwt); jwtRef.current = jwt; // 把jwt存到ref里 getUserDetails(jwt); } catch (err) { console.log(err); } } async function getUserDetails(jwt) { const data = await axios.get(apiValidate + '&JWT=' + jwt); setStateUserInfo(data.data); // 直接更新用户信息,去掉不必要的setTimeout } // 监听stateUserInfo的变化,只有当它存在时才设置token useEffect(() => { // 加个判断避免重复设置token if (stateUserInfo && !stateToken) { setStateToken(jwtRef.current); } }, [stateUserInfo, stateToken]); // 依赖项是stateUserInfo和stateToken,避免无意义的重复执行
为什么要去掉setTimeout?
setTimeout是固定时长的延迟,但React的state更新速度取决于组件复杂度、当前渲染队列情况等因素——4秒可能太长,让用户多等;也可能太短,还是会出现渲染时用户信息未准备好的问题。必须依赖React本身的state更新机制来保证顺序,而不是靠硬编码的延迟。
内容的提问来源于stack exchange,提问作者Rollor

