登录页面后反应路由器 v6 不会切换到主页:警告:无法对未安装的组件执行 React 状态更新
Posted
技术标签:
【中文标题】登录页面后反应路由器 v6 不会切换到主页:警告:无法对未安装的组件执行 React 状态更新【英文标题】:react router v6 won't switch to Home Page after Login Page: Warning: Can't perform a React state update on an unmounted component 【发布时间】:2022-01-05 10:32:19 【问题描述】:注意:使用 firebase v9
在我的 webapp 上,我试图让它在成功登录后将用户重定向到主页,如果不成功,我们会留在登录页面。
我最初只是尝试使用私有路由器,但我相信 currentUser 没有被 useAuth(); 设置;所以它没有被定义。
然后我尝试使用异步等待方法,但我得到了更多的错误。
这是我在控制台日志中得到的错误:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
at RequireAuth (http://localhost:3000/static/js/main.chunk.js:262:82)
这是我在 App.js 中的实现:
import useState from "react";
import "./App.css";
import Signup from "./pages/Signup";
import Login from "./pages/Login";
import BrowserRouter, Routes, Route, Link, Navigate, Outlet from "react-router-dom";
import Home from "./pages/Home";
import useAuth from "./firebase";
function App()
const [mainSection, setMainSection] = useState("home");
const d = new Date();
const time = d.getHours();
if (time >= 18 || time < 5)
document.documentElement.classList.add("dark");
// async function PrivateRoute(children)
// // setLoading(true);
// try
// const currentUser = await useAuth();
// return currentUser ? children : <Navigate to="/login" />;
// // await login(emailRef.current.value, passwordRef.current.value);
// catch
// alert("Error! Authentication failed!!")
//
// // setLoading(false);
//
function RequireAuth()
const currentUser = useAuth();
console.log('currentUser (private router): ', currentUser);
if (!currentUser)
return <Navigate to="/login"/>;
return <Outlet />
// <Home mainSection=mainSection setMainSection=setMainSection />;
// return auth ? children : <Navigate to="/signup" />;
return (
<BrowserRouter>
<Routes>
<Route element=<RequireAuth/>>
<Route path="/" element=<Home mainSection=mainSection setMainSection=setMainSection />/>
</Route>
<Route path="/login" element=<Login /> />
<Route path="/signup" element=<Signup /> />
</Routes>
</BrowserRouter>
);
export default App;
这是我持有 useAuth 的实现:
// Import the functions you need from the SDKs you need
import useEffect, useState from "react";
import initializeApp from "firebase/app";
import getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, onAuthStateChanged from "firebase/auth";
import getFirestore from "firebase/firestore";
import doc, setDoc from "firebase/firestore";
import v4 as uuidv4 from "uuid";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig =
apiKey: "AIzaSyBN30k6RivLOuz7KToi_uD8V5s5cmyD9RM",
authDomain: "auth-development-62c42.firebaseapp.com",
projectId: "auth-development-62c42",
storageBucket: "auth-development-62c42.appspot.com",
messagingSenderId: "414005826367",
appId: "1:414005826367:web:7b987851735426ebedf98a"
;
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth();
export function signup(email, password)
return createUserWithEmailAndPassword(auth, email, password);
export function login(email, password)
return signInWithEmailAndPassword(auth, email, password);
// eventually write a logout function
export async function sendHabitToFirestore(uidPath, habitName)
const db = getFirestore();
const habitId = uuidv4();
const pathDocRef = doc(db, "users", uidPath, "user_habits", habitId);
//initialize habitValues for the whole year
const habitValues = []
var d = new Date();
var year = d.getFullYear();
for (let day = 1; day <= 31; day++)
var dateString = year.toString() + '-01-' + day.toString();
var value = date: dateString, completed: false ;
habitValues.push(value);
await setDoc(pathDocRef,
name: habitName,
id: habitId,
calendarData: habitValues,
);
export function useAuth()
const [currentUser, setCurrentUser ] = useState();
useEffect(() =>
const unsub = onAuthStateChanged(auth, user => setCurrentUser(user));
return unsub;
, [])
return currentUser;
export default getFirestore();
【问题讨论】:
【参考方案1】:问题
所以我收集到用户在“/login”路径上并进行身份验证,然后您将他们重定向到主路径"/"
。如果我正确阅读了代码,那么此时RequireAuth
包装器会挂载并检查当前用户。初始的currentUser
状态是未定义的,或者换句话说是一个虚假值,并且<Navigate to="/login"/>
被渲染并且用户被重定向返回 到登录页面。这卸载RequireAuth
,但可能存在延迟入队状态更新。
解决方案
将RequireAuth
移到外面 App
。我认为这不是您问题的直接原因,但在其他 React 组件中声明 React 组件只是一种反模式。每次App
重新呈现时,您实际上 都在声明一个新 RequireAuth
组件,并且任何组件状态都将丢失。
要解决路由问题,请添加一个已验证或未验证的不确定“待处理”状态,并且在包装器组件确认 currentUser
身份验证状态之前,不要承诺呈现出口或重定向。使用 undefined
可以解决此问题,因为它既不是用户对象也不是 null 且没有用户。
function RequireAuth()
const currentUser = useAuth();
if (currentUser === undefined)
return null; // or loading indicator, etc...
return currentUser ? <Outlet /> : <Navigate to="/login"/>;
【讨论】:
以上是关于登录页面后反应路由器 v6 不会切换到主页:警告:无法对未安装的组件执行 React 状态更新的主要内容,如果未能解决你的问题,请参考以下文章