页面刷新时useEffect异常及react-typed无限循环问题求助
问题分析
- 刷新页面报错的核心原因:AuthContext 中的
user在页面刷新后是异步初始化的,初始值为undefined。此时useEffect依赖user执行时,直接访问user.uid会触发「读取undefined的属性」错误,导致组件渲染失败,进而引发react-typed内部的异常(控制台的indexOf报错是连锁反应)。 - 移除
user依赖后出现无限循环:因为useEffect没有依赖限制,每次组件渲染都会执行,而setUserDetails会更新状态触发重新渲染,形成循环。
正确的 useEffect 用法及修复方案
- 添加用户存在性判断:在
useEffect内部先检查user是否存在,避免在未初始化时执行数据库操作。 - 保留
user在依赖数组中:确保仅当user变化时才重新获取用户信息,避免不必要的重复请求和无限循环。 - 处理异步操作的取消(可选但推荐):添加组件卸载时的清理逻辑,防止组件卸载后异步请求仍在进行导致内存泄漏。
- 优化用户信息初始状态:避免渲染时
userDetails.displayName为undefined,可设置初始状态为null并添加加载状态或默认值。
修改后的完整代码
import React, { useState, useEffect } from "react"; import Typed from "react-typed"; import { UserAuth } from "../contexts/AuthContext"; import { db } from "../utils/firebase/firebase.utils"; import { doc, getDoc } from "firebase/firestore"; const Hero = () => { const [userDetails, setUserDetails] = useState(null); const { user } = UserAuth(); useEffect(() => { // 当user未初始化时,直接返回,避免报错 if (!user) return; let isMounted = true; // 标记组件是否挂载,用于清理异步操作 const fetchData = async () => { try { const docRef = doc(db, "users", user.uid); const docSnap = await getDoc(docRef); // 仅当组件仍挂载时更新状态,防止内存泄漏 if (isMounted && docSnap.exists()) { setUserDetails(docSnap.data()); } } catch (error) { console.error("获取用户信息失败:", error); } }; fetchData(); // 组件卸载时的清理逻辑 return () => { isMounted = false; }; }, [user]); // 保留user作为依赖,确保仅当user变化时执行 // 加载状态或默认显示 if (!user || !userDetails) { return ( <div className="text-white h-screen flex items-center justify-center"> <p className="md:text-5xl sm:text-4xl text-xl font-bold">加载中...</p> </div> ); } return ( <div className="text-white"> <div className="max-w-[800px] mt-[-96px] w-full h-screen mx-auto text-center flex flex-col justify-center"> <div className="flex justify-center items-center"> <Typed className="md:text-5xl sm:text-4xl text-xl font-bold md:pl-4 pl-2" strings={["Hello,", "Hola,"]} typeSpeed={120} backSpeed={140} loop /> <p className="md:text-5xl sm:text-4xl text-xl font-bold py-4"> {userDetails.displayName}! </p> </div> </div> </div> ); }; export default Hero;
关键说明
- 用户存在性判断:
if (!user) return确保只有当user完成初始化后才执行数据库请求,从根源避免user.uid的访问错误。 - 组件挂载标记:
isMounted变量防止组件卸载后异步请求完成仍更新状态,避免内存泄漏和控制台警告。 - 加载状态处理:通过
if (!user || !userDetails)显示加载提示,提升用户体验,同时避免渲染未完成的用户信息。
内容的提问来源于stack exchange,提问作者alexsmt
相关产品推荐
相关产品推荐

