如何在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
相关产品推荐
相关产品推荐

