无法对 Firebase onAuthStateChanged 上的未安装组件错误执行 React 状态更新

Can't perform a React state update on an unmounted component Error on Firebase onAuthStateChanged

我正在尝试在用户成功登录后路由到“/”。

目前,在我的 Login.JS 文件中,我有 handleSubmit 函数从表单中获取结果:

 async function handleSubmit(e) {
        e.preventDefault()

        try {
            setError("")
            setLoading(true)
            await login(emailRef.current.value, passwordRef.current.value)
            history.push("/")
        } catch(error) {
            console.log(error)
            setError("Failed to Log In")
        }
        setLoading(false)
    }

然后,我有一个传递登录上下文的 AuthContext

import React, { useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, getAuth, signInWithEmailAndPassword} from 'firebase/auth';
import app from '../firebase'

const AuthContext = React.createContext()

export function useAuth() {
    return useContext(AuthContext)
}

export function AuthProvider({ children }) {
    const auth = getAuth()
    const [currentUser, setCurrentUser] = useState()
    const [loading, setLoading] = useState(true)

    function login(email, password) {
        return signInWithEmailAndPassword(auth, email, password)
    }

    useEffect(() => {
        const unsubscribe = onAuthStateChanged(auth, (user) => {
            setCurrentUser(user)
            setLoading(false)
        })
        return unsubscribe;
    }, [auth])
   

    const value = {
        currentUser,
        login
    }
    return (
        <AuthContext.Provider value={value}>
            {!loading && children}
        </AuthContext.Provider>
    )
}

我可以看到用户可以登录,但是,它不会在“/”页面中呈现任何内容,并在控制台中显示此错误消息:

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,取消 useEffect 清理函数中的所有订阅和异步任务。

useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
        setCurrentUser(user)
        setLoading(false)
    })
    return unsubscribe;
}, [auth])

您不能在 useEffect 挂钩中使用 return 语句而不是清理函数。

有两个可能的地方可能导致此问题;我不确定是哪一个。

第一种可能:在使用效果中,你背靠背设置了两次state。 React 尝试 批处理多个设置状态并进行单个渲染,但由于这是一个不受 React 控制的异步回调,因此它不能在此处这样做。因此,您可能会获得一个用于更改用户的渲染,然后是另一个用于更改加载的渲染。如果第一次渲染导致组件卸载,则可能会导致您设置加载时出错。

在 React 18 中,这个批处理问题将消失,但在那之前,您可以像这样将两个设置状态作为一个组发生:

import { unstable_batchedUpdates } from "react-dom";
// ...

useEffect(() => {
  const unsubscribe = onAuthStateChanged(auth, (user) => {
    unstable_batchedUpdates(() => {
      setCurrentUser(user);
      setLoading(false);
    });
  });
  return unsubscribe;
}, [auth]);

第二种可能:可能在handleSubmit中。您设置一些状态,启动登录并等待它,推送到历史记录,然后再次设置状态。如果组件在等待 promise 时卸载,或者在推送到历史记录时卸载,您将遇到此问题。如果这是原因,那么您可以拥有一个在组件卸载时更新的引用,并在执行最终设置状态之前检查该引用:

const mounted = useRef(true);
useEffect(() => { 
  return () => {
    mounted.current = false;
  }
}, []);

async function handleSubmit(e) {
  e.preventDefault();

  try {
    setError("");
    setLoading(true);
    await login(emailRef.current.value, passwordRef.current.value);
    history.push("/");
  } catch (error) {
    console.log(error);
    if (mounted.current) {
      setError("Failed to Log In");
    }
  }
  if (mounted.current) {
    setLoading(false);
  }
}

P.S, they will be removing this warning from react 因为它导致的所有误报,你的情况就是其中之一。您的代码中没有实际的内存泄漏。您在卸载后设置了一次状态,无害地忽略了它,然后就是这样您正确地拆除了您的 onAuthStateChanged 侦听器。