## 主要改动 ### 新增 - 创建 `store/slices/authModalSlice.js` - Redux Slice 管理认证弹窗状态 - 创建 `hooks/useAuthModal.js` - 自定义 Hook,组合 Redux 状态和业务逻辑 ### 修改 - 更新 `store/index.js` - 添加 authModal reducer - 更新 `App.js` - 移除 AuthModalProvider 包裹层 - 更新 5 个组件的 import 路径: - AuthFormContent.js - AuthModalManager.js - WechatRegister.js - HomeNavbar.js - ProtectedRoute.js ### 删除 - 删除 `contexts/AuthModalContext.js` - 旧的 Context 实现 ## 迁移效果 - ✅ 减少 Provider 嵌套层级(4层 → 3层) - ✅ 统一状态管理架构(Redux) - ✅ 更好的调试体验(Redux DevTools) - ✅ 保持 API 兼容性(无破坏性修改) ## 技术细节 - 使用 `useRef` 存储 `onSuccessCallback`(函数不可序列化) - 保持与 AuthContext 的依赖关系(AuthProvider 暂未迁移) - 所有业务逻辑保持不变,仅改变状态管理方式 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
117 lines
3.7 KiB
JavaScript
117 lines
3.7 KiB
JavaScript
// src/hooks/useAuthModal.js
|
||
// 认证弹窗自定义 Hook - 组合 Redux 状态和业务逻辑
|
||
|
||
import { useCallback, useRef } from 'react';
|
||
import { useDispatch, useSelector } from 'react-redux';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import {
|
||
openModal,
|
||
closeModal,
|
||
selectAuthModalOpen,
|
||
selectRedirectUrl
|
||
} from '../store/slices/authModalSlice';
|
||
import { useAuth } from '../contexts/AuthContext';
|
||
import { logger } from '../utils/logger';
|
||
|
||
/**
|
||
* 认证弹窗自定义 Hook
|
||
*
|
||
* 功能:
|
||
* - 管理认证弹窗的开关状态
|
||
* - 处理登录成功后的回调和跳转
|
||
* - 未登录时关闭弹窗自动跳转到首页
|
||
*
|
||
* 注意:
|
||
* - onSuccessCallback 使用 ref 存储(函数不可序列化,不能放 Redux)
|
||
* - 依赖 AuthContext 读取 isAuthenticated(AuthProvider 暂未迁移)
|
||
*
|
||
* @returns {object} 弹窗状态和操作方法
|
||
*/
|
||
export const useAuthModal = () => {
|
||
const dispatch = useDispatch();
|
||
const navigate = useNavigate();
|
||
|
||
// Redux 状态
|
||
const isAuthModalOpen = useSelector(selectAuthModalOpen);
|
||
const redirectUrl = useSelector(selectRedirectUrl);
|
||
|
||
// AuthContext 状态(暂未迁移到 Redux)
|
||
const { isAuthenticated } = useAuth();
|
||
|
||
// 使用 ref 存储回调函数(不能放 Redux,因为函数不可序列化)
|
||
const onSuccessCallbackRef = useRef(null);
|
||
|
||
/**
|
||
* 打开认证弹窗(统一的登录/注册入口)
|
||
* @param {string} url - 认证成功后的重定向URL(可选)
|
||
* @param {function} callback - 认证成功后的回调函数(可选)
|
||
*/
|
||
const openAuthModal = useCallback((url = null, callback = null) => {
|
||
onSuccessCallbackRef.current = callback;
|
||
dispatch(openModal({ redirectUrl: url }));
|
||
|
||
logger.debug('useAuthModal', '打开认证弹窗', {
|
||
redirectUrl: url || '无',
|
||
hasCallback: !!callback
|
||
});
|
||
}, [dispatch]);
|
||
|
||
/**
|
||
* 关闭认证弹窗
|
||
* 如果用户未登录,跳转到首页
|
||
*/
|
||
const closeAuthModal = useCallback(() => {
|
||
dispatch(closeModal());
|
||
onSuccessCallbackRef.current = null;
|
||
|
||
// ⭐ 如果用户关闭弹窗时仍未登录,跳转到首页
|
||
if (!isAuthenticated) {
|
||
navigate('/home');
|
||
logger.debug('useAuthModal', '未登录关闭弹窗,跳转到首页');
|
||
} else {
|
||
logger.debug('useAuthModal', '关闭认证弹窗');
|
||
}
|
||
}, [dispatch, isAuthenticated, navigate]);
|
||
|
||
/**
|
||
* 登录/注册成功处理
|
||
* @param {object} user - 用户信息
|
||
*/
|
||
const handleLoginSuccess = useCallback((user) => {
|
||
// 执行自定义回调(如果有)
|
||
if (onSuccessCallbackRef.current) {
|
||
try {
|
||
onSuccessCallbackRef.current(user);
|
||
logger.debug('useAuthModal', '执行成功回调', {
|
||
userId: user?.id
|
||
});
|
||
} catch (error) {
|
||
logger.error('useAuthModal', 'handleLoginSuccess 回调执行失败', error, {
|
||
userId: user?.id,
|
||
hasCallback: !!onSuccessCallbackRef.current
|
||
});
|
||
}
|
||
}
|
||
|
||
// ⭐ 登录成功后,只关闭弹窗,留在当前页面(不跳转)
|
||
// 移除了原有的 redirectUrl 跳转逻辑
|
||
dispatch(closeModal());
|
||
onSuccessCallbackRef.current = null;
|
||
|
||
logger.debug('useAuthModal', '登录成功,关闭弹窗', {
|
||
userId: user?.id
|
||
});
|
||
}, [dispatch]);
|
||
|
||
return {
|
||
// 状态
|
||
isAuthModalOpen,
|
||
redirectUrl,
|
||
|
||
// 方法
|
||
openAuthModal,
|
||
closeModal: closeAuthModal,
|
||
handleLoginSuccess,
|
||
};
|
||
};
|