React中useEffect清理函数为何在渲染时触发?
解决React 18 Strict Mode下useEffect清理函数误触发导致状态无法更新的问题
你的问题根源是React 18开发环境的Strict Mode特性:它会刻意重复执行组件的挂载→卸载→挂载流程,用来检测潜在的组件bug。这就导致你的useSignin钩子中,空依赖的useEffect清理函数被提前触发,把unmounted状态设为true,但组件实际仍处于挂载状态,后续异步登录操作完成后,因unmounted为true无法更新loading和error状态。
解决方案:用Ref替代State跟踪组件挂载状态
useState的更新会触发重渲染,且受闭包影响,不适合用来跟踪组件挂载状态。改用useRef可以避免这些问题,因为Ref的current属性是可变的,不会触发重渲染,且在组件生命周期内保持引用一致性。
修改后的useSignin钩子代码:
import { useEffect, useState, useRef } from "react"; import { useAuth } from "./useAuth"; import { auth } from "../firebase/config"; export default function useSignin() { // 用Ref跟踪组件是否挂载 const isMountedRef = useRef(true); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const { dispatch } = useAuth(); const signIn = async (email, password) => { setLoading(true); setError(null); try { const res = await auth.signInWithEmailAndPassword(email, password); dispatch({ type: "LOGIN", payload: res.user, }); // 只有组件挂载时才更新状态 if (isMountedRef.current) { setLoading(false); setError(null); } } catch (err) { if (isMountedRef.current) { setError(err.message); setLoading(false); } } }; useEffect(() => { // 组件卸载时标记为未挂载 return () => { isMountedRef.current = false; }; }, []); return { signIn, error, loading }; }
为什么这个方案有效?
useRef的current属性不会触发组件重渲染,避免了Strict Mode下的状态重置问题。- 它不受闭包限制,异步操作完成时能准确读取当前组件的挂载状态。
- 严格遵循了React的最佳实践,既保留了防止卸载后更新状态的逻辑,又适配了Strict Mode的特性。
临时方案(不推荐):关闭Strict Mode
如果你只是临时测试,可以移除项目入口文件(通常是index.js)中的<StrictMode>标签,但不建议长期这么做——Strict Mode能帮你提前发现很多潜在的组件问题。
内容的提问来源于stack exchange,提问作者MoChahadeh
相关产品推荐
相关产品推荐

