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

Expo应用未挂载组件报错崩溃、Firebase监听器取消后仍生效问题求助

问题根源
  • 启动屏(SplashLoadingScreen)的Firebase auth状态监听器没有添加useEffect清理逻辑,组件卸载后监听器仍在运行,引发内存泄漏和跨页面触发
  • 启动屏中存储取消订阅函数的变量为普通var类型,存在闭包陷阱,部分场景下回调中无法正确拿到取消订阅函数
  • 注册页(SignUpScreen)登录成功后重复创建auth状态监听器,且该监听器同样没有清理逻辑,同时完全没有必要新增监听器,直接处理登录接口返回结果即可
  • 注册页的通知权限申请函数直接在组件顶层执行,每次重渲染都会重复调用,容易触发已卸载组件的状态更新
修复方案

1. 修复启动屏的监听器逻辑

首先用useRef存储取消订阅函数避免闭包问题,同时在useEffect的清理回调中执行取消订阅,保证组件卸载时一定会销毁监听器:

// 启动屏组件内新增ref存储取消订阅函数
const unsubscribeRef = useRef(null);

// 替换原跳转逻辑useEffect
useEffect(() => {
  if (!isReady) return;
  try {
    unsubscribeRef.current = firebase.auth().onAuthStateChanged((user) => {          
      if (user) {
        firebase.firestore().collection("User Details").doc(user.uid).get().then((snap) => {
          try {
            // 执行跳转前先销毁监听器
            if (unsubscribeRef.current) {
              unsubscribeRef.current();
              unsubscribeRef.current = null;
            }
            if(snap.exists){
              navigation.reset({
                index : 0,
                routes : [{ name : 'Tab'}],
              });
            }else {
              navigation.reset({
                index : 0,
                routes : [{ name : 'User'}],
              });
            }
          } catch(e) {console.log(e)}
        });
        return;
      }
      // 无用户场景也要先销毁监听器再跳转
      if (unsubscribeRef.current) {
        unsubscribeRef.current();
        unsubscribeRef.current = null;
      }
      navigation.reset({
        index : 0,
        routes : [{ name : 'SignUp'}],
      });
    });
  } catch(e) {console.log(e)}

  // 重点:组件卸载时强制清理监听器
  return () => {
    if (unsubscribeRef.current) {
      unsubscribeRef.current();
      unsubscribeRef.current = null;
    }
  };
}, [isReady, navigation]);

2. 修复注册页的重复监听问题

删除冗余的confirmation函数,直接在ConfirmCode中处理登录结果,不需要新增auth状态监听器,同时把通知权限申请函数移入useEffect避免重复执行:

// 注册页组件内新增挂载标记位,避免已卸载组件更新状态
const isMountedRef = useRef(true);

useEffect(() => {
  registerForPushNotificationsAsync();
  // 组件卸载时更新标记位
  return () => {
    isMountedRef.current = false;
  };
}, []);

// 替换原ConfirmCode逻辑
const ConfirmCode = async () => {
  if (!isMountedRef.current) return;
  setIsLoading(true);
  setConfirmError(undefined);
  setConfirmInProgress(true);
  const credential = firebase.auth.PhoneAuthProvider.credential(
    verificationId,
    verificationCode
  );

  try {
    const userCred = await firebase.auth().signInWithCredential(credential);
    const snap = await firebase.firestore().collection("User Details").doc(userCred.user.uid).get();
    if(snap.exists){
      navigation.reset({
        index : 0,
        routes : [{ name : 'Tab'}],
      });
    }else {
      navigation.reset({
        index : 0,
        routes : [{ name : 'User'}],
      });
    }
  } catch (err) {
    if (isMountedRef.current) {
      setConfirmError(err);
    }
    console.log(err);
  } finally {
    if (isMountedRef.current) {
      setConfirmInProgress(false);
      setVerificationId('');
      setVerificationCode('');
      verificationCodeTextInput.current?.clear();
      setIsLoading(false);
    }
  }
}

内容的提问来源于stack exchange,提问作者Deep Togani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 19:06:01