You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React自定义Hook加载完成后再执行后续函数?

问题根源

你现在的写法存在几个核心逻辑错误,导致无法等待Hook加载完成:

  • 点击事件回调执行时,捕获的是点击瞬间的state快照,后续Hook里loading、userDef更新后,已经执行完的回调不会重新触发判断
  • setUserID是异步状态更新,调用后立刻读取userId拿到的还是旧值,不会立刻触发Hook拉取最新用户资料
  • 原自定义Hook没有做Firebase监听的卸载清理,也没处理userid为空、用户资料不存在的边界情况,容易引发内存泄漏和运行报错
  • 不要用while循环等待状态变化,会直接阻塞JS主线程导致页面卡死,渲染和状态更新都无法执行,反而永远等不到结果

修复实现

第一步:修复自定义Hook逻辑

补全边界判断和副作用清理逻辑:

import { useEffect, useState } from "react"
import { database } from "../../services/firebase"

type UserDetails = {
    id: string,
    userID: string,
    userName: string,
    userEmail: string,
    userAvatar: string,
    userDescription: string,
    userSkills: string,
    userPhone: string,
    userTitle: string,
    userInterests: string
}

export function useGetUserProfile(userid: string | undefined){
    const [ userDef, setUserDef ] = useState<UserDetails | null>(null)
    const [ loading, setLoading ] = useState<boolean>(false)

    useEffect(()=>{
        // 无有效userid时重置状态,不发起无效请求
        if (!userid) {
            setUserDef(null)
            setLoading(false)
            return
        }
        setLoading(true)
        const userRef = database.ref(`users/${userid}`);
        const handleValueChange = (userInfo: any) =>{
            const databaseUser = userInfo.val();
            // 处理用户资料不存在的场景
            if (!databaseUser) {
                setUserDef(null)
                setLoading(false)
                return
            }
            const userDetails: UserDetails = {
                id: databaseUser.key,
                userID: databaseUser.userID,
                userName: databaseUser.userName,
                userEmail: databaseUser.userEmail,
                userAvatar: databaseUser.userAvatar,
                userDescription: databaseUser.userDescription,
                userSkills: databaseUser.userSkills, 
                userPhone: databaseUser.userPhone,
                userTitle: databaseUser.userTitle,
                userInterests: databaseUser.userInterests
            }
            setUserDef(userDetails);
            setLoading(false);
        }
        userRef.on('value', handleValueChange)
        // 组件卸载/依赖变化时取消监听,避免内存泄漏
        return () => userRef.off('value', handleValueChange)
    },[userid])
    return{userDef, loading}
}

第二步:拆分业务逻辑,用副作用监听状态完成跳转

不要把跳转逻辑写在点击事件里,所有依赖异步状态结果的逻辑,都放在useEffect中监听状态变化执行:

import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom' // 匹配你使用的路由库导入
// 其余导入省略

// 组件内部
const navigate = useNavigate()
const [ userId, setUserID ] = useState<string | undefined>()
const { userDef, loading } = useGetUserProfile(userId)

async function handleAuthGoogle(){
    // 点击事件只负责触发登录和更新userId,不做跳转判断
    if(!user){
        await singIngWithGoogle();
    }
    // 登录拿到有效user对象后再更新userId,触发Hook拉取用户资料
    setUserID(user?.id)
}

// 监听所有依赖状态,等异步流程全部完成再执行跳转
useEffect(() => {
    // 三个前置条件:有有效userId、资料请求加载完成、已拿到登录用户对象
    if (userId && !loading && user) {
        if(userDef){
            navigate('/home');
        }else{
            navigate(`/Profile/Editar/${userId}`)
        }
    }
}, [userId, loading, userDef, user, navigate])

内容的提问来源于stack exchange,提问作者Gabriel Donada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:12:25