feat: user 依赖优化
This commit is contained in:
@@ -489,6 +489,11 @@ export default function HomeNavbar() {
|
||||
const brandHover = useColorModeValue('blue.600', 'blue.300');
|
||||
const toast = useToast();
|
||||
|
||||
// ⚡ 提取 userId 为独立变量,避免 user 对象引用变化导致无限循环
|
||||
const userId = user?.id;
|
||||
const prevUserIdRef = React.useRef(userId);
|
||||
const prevIsAuthenticatedRef = React.useRef(isAuthenticated);
|
||||
|
||||
// 添加调试信息
|
||||
logger.debug('HomeNavbar', '组件渲染状态', {
|
||||
hasUser: !!user,
|
||||
@@ -727,29 +732,44 @@ export default function HomeNavbar() {
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}, [isAuthenticated, user?.id]); // 只依赖 user.id,避免 user 对象变化导致无限循环
|
||||
}, [isAuthenticated, userId]); // ⚡ 使用 userId 而不是 user?.id
|
||||
|
||||
// 监听用户变化,重置检查标志(用户切换或退出登录时)
|
||||
React.useEffect(() => {
|
||||
const userIdChanged = prevUserIdRef.current !== userId;
|
||||
const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated;
|
||||
|
||||
if (userIdChanged || authChanged) {
|
||||
prevUserIdRef.current = userId;
|
||||
prevIsAuthenticatedRef.current = isAuthenticated;
|
||||
|
||||
if (!isAuthenticated || !user) {
|
||||
// 用户退出登录,重置标志
|
||||
hasCheckedCompleteness.current = false;
|
||||
setProfileCompleteness(null);
|
||||
setShowCompletenessAlert(false);
|
||||
}
|
||||
}, [isAuthenticated, user?.id]); // 监听用户 ID 变化
|
||||
}
|
||||
}, [isAuthenticated, userId, user]); // ⚡ 使用 userId
|
||||
|
||||
// 用户登录后检查资料完整性
|
||||
React.useEffect(() => {
|
||||
if (isAuthenticated && user) {
|
||||
const userIdChanged = prevUserIdRef.current !== userId;
|
||||
const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated;
|
||||
|
||||
if ((userIdChanged || authChanged) && isAuthenticated && user) {
|
||||
// 延迟检查,避免过于频繁
|
||||
const timer = setTimeout(checkProfileCompleteness, 1000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [isAuthenticated, user?.id, checkProfileCompleteness]); // 只依赖 user.id,避免无限循环
|
||||
}, [isAuthenticated, userId, checkProfileCompleteness, user]); // ⚡ 使用 userId
|
||||
|
||||
// 加载订阅信息
|
||||
React.useEffect(() => {
|
||||
const userIdChanged = prevUserIdRef.current !== userId;
|
||||
const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated;
|
||||
|
||||
if (userIdChanged || authChanged) {
|
||||
if (isAuthenticated && user) {
|
||||
const loadSubscriptionInfo = async () => {
|
||||
try {
|
||||
@@ -785,7 +805,8 @@ export default function HomeNavbar() {
|
||||
is_active: true
|
||||
});
|
||||
}
|
||||
}, [isAuthenticated, user?.id]); // 只依赖 user.id 而不是整个 user 对象
|
||||
}
|
||||
}, [isAuthenticated, userId, user]); // ⚡ 使用 userId,防重复通过 ref 判断
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/components/ProtectedRoute.js - 弹窗拦截版本
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Box, VStack, Spinner, Text } from '@chakra-ui/react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
@@ -8,15 +8,17 @@ const ProtectedRoute = ({ children }) => {
|
||||
const { isAuthenticated, isLoading, user } = useAuth();
|
||||
const { openAuthModal, isAuthModalOpen } = useAuthModal();
|
||||
|
||||
// 记录当前路径,登录成功后可以跳转回来
|
||||
const currentPath = window.location.pathname + window.location.search;
|
||||
// ⚡ 使用 useRef 保存当前路径,避免每次渲染创建新字符串导致 useEffect 无限循环
|
||||
const currentPathRef = useRef(window.location.pathname + window.location.search);
|
||||
|
||||
// 未登录时自动弹出认证窗口
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isAuthenticated && !user && !isAuthModalOpen) {
|
||||
openAuthModal(currentPath);
|
||||
openAuthModal(currentPathRef.current);
|
||||
}
|
||||
}, [isAuthenticated, user, isLoading, isAuthModalOpen, currentPath, openAuthModal]);
|
||||
// ⚠️ 移除 user 依赖,因为 user 对象每次从 API 返回都是新引用,会导致无限循环
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isAuthenticated, isLoading, isAuthModalOpen, openAuthModal]);
|
||||
|
||||
// 显示加载状态
|
||||
if (isLoading) {
|
||||
|
||||
@@ -26,6 +26,9 @@ export const AuthProvider = ({ children }) => {
|
||||
const toast = useToast();
|
||||
const { showWelcomeGuide } = useNotification();
|
||||
|
||||
// ⚡ 使用 ref 保存最新的 isAuthenticated 值,避免事件监听器重复注册
|
||||
const isAuthenticatedRef = React.useRef(isAuthenticated);
|
||||
|
||||
// 检查Session状态
|
||||
const checkSession = async () => {
|
||||
try {
|
||||
@@ -57,19 +60,27 @@ export const AuthProvider = ({ children }) => {
|
||||
});
|
||||
|
||||
if (data.isAuthenticated && data.user) {
|
||||
setUser(data.user);
|
||||
setIsAuthenticated(true);
|
||||
// ⚡ 只在 user 数据真正变化时才更新状态,避免无限循环
|
||||
setUser((prevUser) => {
|
||||
// 比较用户 ID,如果相同则不更新
|
||||
if (prevUser && prevUser.id === data.user.id) {
|
||||
return prevUser;
|
||||
}
|
||||
return data.user;
|
||||
});
|
||||
setIsAuthenticated((prev) => prev === true ? prev : true);
|
||||
} else {
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
setUser((prev) => prev === null ? prev : null);
|
||||
setIsAuthenticated((prev) => prev === false ? prev : false);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('AuthContext', 'checkSession', error);
|
||||
// 网络错误或超时,设置为未登录状态
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
setUser((prev) => prev === null ? prev : null);
|
||||
setIsAuthenticated((prev) => prev === false ? prev : false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
// ⚡ 只在 isLoading 为 true 时才设置为 false,避免不必要的状态更新
|
||||
setIsLoading((prev) => prev === false ? prev : false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,11 +90,17 @@ export const AuthProvider = ({ children }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// ⚡ 同步 isAuthenticated 到 ref
|
||||
useEffect(() => {
|
||||
isAuthenticatedRef.current = isAuthenticated;
|
||||
}, [isAuthenticated]);
|
||||
|
||||
// 监听路由变化,检查session(处理微信登录回调)
|
||||
// ⚡ 移除 isAuthenticated 依赖,使用 ref 避免重复注册事件监听器
|
||||
useEffect(() => {
|
||||
const handleRouteChange = () => {
|
||||
// 如果是从微信回调返回的,重新检查session
|
||||
if (window.location.pathname === '/home' && !isAuthenticated) {
|
||||
// 使用 ref 获取最新的认证状态
|
||||
if (window.location.pathname === '/home' && !isAuthenticatedRef.current) {
|
||||
checkSession();
|
||||
}
|
||||
};
|
||||
@@ -91,7 +108,7 @@ export const AuthProvider = ({ children }) => {
|
||||
window.addEventListener('popstate', handleRouteChange);
|
||||
return () => window.removeEventListener('popstate', handleRouteChange);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isAuthenticated]);
|
||||
}, []); // ✅ 空依赖数组,只注册一次事件监听器
|
||||
|
||||
// 更新本地用户的便捷方法
|
||||
const updateUser = (partial) => {
|
||||
|
||||
Reference in New Issue
Block a user