无法对 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 侦听器。
我正在尝试在用户成功登录后路由到“/”。
目前,在我的 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 侦听器。