快速切换userId后React useState状态停止更新问题
问题原因
快速切换userId时,多个查询请求会并发执行。旧请求的响应可能晚于新请求返回,此时旧请求的onCompleted回调会调用setUser,将已经更新的新用户状态覆盖,导致界面显示卡住。
解决方案
可以通过以下方式避免旧请求的响应干扰当前状态:
方案1:用useRef记录当前userId,过滤旧请求结果
在组件中用ref保存当前的userId,当请求完成时,只有当返回结果对应的userId和当前ref中的值一致时,才更新状态:
interface SingleUserOverviewProps { userId: string } function SingleUserOverview({ userId }: SingleUserOverviewProps) { const [user, setUser] = useState<UserOverviewProps>() const currentUserIdRef = useRef(userId) // 同步ref的userId值 useEffect(() => { currentUserIdRef.current = userId }, [userId]) const { data, loading, error } = useUserQuery({ variables: { userId }, onCompleted: (data) => { // 仅当当前组件的userId和请求的userId一致时才更新状态 if (data.user && currentUserIdRef.current === userId) { setUser(data.user) } }, }) return ( <> {user ? (<div>{user.name}</div>) : null} </> ) }
方案2:监听userId变化,先清空用户状态
每次userId切换时,先把user状态置为undefined,确保新请求回来前不会显示旧数据,同时避免旧请求覆盖新状态:
interface SingleUserOverviewProps { userId: string } function SingleUserOverview({ userId }: SingleUserOverviewProps) { const [user, setUser] = useState<UserOverviewProps>() // userId变化时清空user状态 useEffect(() => { setUser(undefined) }, [userId]) const { data, loading, error } = useUserQuery({ variables: { userId }, onCompleted: (data) => { if (data.user) { setUser(data.user) } }, }) return ( <> {user ? (<div>{user.name}</div>) : null} </> ) }
额外优化:结合查询库的取消请求能力
如果你使用的是Apollo Client这类库,useQuery默认会在组件卸载或变量变化时取消旧请求,也可以显式配置fetchPolicy确保每次获取最新数据,进一步避免旧请求干扰:
const { data, loading, error } = useUserQuery({ variables: { userId }, fetchPolicy: 'network-only', // 每次都从网络获取最新数据 onCompleted: (data) => { if (data.user) { setUser(data.user) } }, })
这样既保留了useState用于手动触发更新(比如按钮点击更新后端数据后,你可以调用refetch或者直接更新状态),又解决了快速切换userId时的状态卡住问题。
内容的提问来源于stack exchange,提问作者sev
相关产品推荐
相关产品推荐

