切换账号后useEffect执行异常,日志输出不符预期求助
问题
切换不同账号重新登录时,页面会先3次渲染旧账号数据,之后才显示当前账号数据,引发不必要的网络请求及诸多Bug。经排查,唯一设置userData的useEffect函数如下:
useEffect(() => { if (userData?.status === 500) { setItemToLocalStorage("token", "") history.push("/login") } let updatedState = {...state} console.log(`${updatedState?.isAdmin} before`) if (userData && userDataFetched) { updatedState = { ...updatedState, isAdmin: userData?.isQappAdmin, userRole: userData?.roles[0], userName: userData?.userName } if(userData?.roles[0] !== IRoles.WASHER_MANAGER) { updatedState = { ...updatedState, user: userData } } } console.log(`${updatedState?.isAdmin} after`) setState(updatedState) }, [userData])
日志输出:
false before true after true before false after
请问该useEffect为何会出现这种异常情况?
异常原因分析
- 闭包捕获旧state引发更新混乱:
useEffect每次执行时,捕获的state是组件上一轮渲染的闭包值,并非最新状态。切换账号时,新userData触发useEffect,但此时state还保留着旧账号的信息,导致第一次更新是旧state与新userData的合并;若存在未取消的旧账号请求,旧userData会再次触发useEffect,这时state已更新为新账号状态,合并旧userData后又变回旧状态,就出现了日志里isAdmin值来回切换的情况。 - 依赖数组不完整:
useEffect内部用到了userDataFetched,但未将其加入依赖数组。这会导致userDataFetched变化时,useEffect不会重新执行,可能在userDataFetched为false时错误保留旧state,或在数据已获取完成后无法触发正确的状态更新。 - 状态更新方式错误:直接通过
{...state}创建副本更新状态,而非使用函数式更新setState(prevState => {...})。当多次setState连续触发时,闭包捕获的state不是最新值,会导致后续更新覆盖前面的结果,引发状态来回跳转。 - 未处理请求竞态问题:切换账号时,旧账号的请求可能仍在进行,若未取消旧请求,当旧请求晚于新请求返回时,旧
userData会覆盖新数据,导致useEffect再次执行,渲染旧账号信息。
内容的提问来源于stack exchange,提问作者Nika
相关产品推荐
相关产品推荐

