# 🔧 StreamerConsole React Hooks 错误修复报告 ## 问题描述 用户在访问主播控制台页面时遇到页面空白,浏览器控制台报错: ``` React has detected a change in the order of Hooks called by StreamerConsole Rendered more hooks than during the previous render ``` ## 根本原因分析 ### 1. React Hooks规则违反 - **错误类型**: 在条件渲染后定义了新的Hook - **问题位置**: `StreamerConsole.tsx` 第175-265行 - **具体表现**: - `loading`检查后定义`activeChests` - 在条件渲染后又定义了`countdowns`等状态Hook - 违反了"Hooks必须始终在组件顶层调用"的规则 ### 2. 类型定义错误 - **问题**: 使用了`NodeJS.Timeout`类型 - **影响**: 浏览器环境中该类型不存在 - **报错**: `Cannot find namespace 'NodeJS'` ## 修复方案 ### 1. 重构Hook声明顺序 **修复前**(错误): ```typescript const StreamerConsole = () => { // 基础状态Hook const [myChests, setMyChests] = useState([]); const [loading, setLoading] = useState(true); // ... 其他Hook // 权限检查(条件渲染) useEffect(() => { if (!user) { navigate('/login'); return; } loadMyChests(); }, [user, navigate]); // 加载状态检查(条件渲染) if (loading) { return ; } // 计算活跃宝箱(条件渲染后) const activeChests = myChests.filter(c => c.status === 0 || c.status === 1); // ❌ 错误:在这里定义新的Hook! const [countdowns, setCountdowns] = useState<{[key: number]: number}>({}); const countdownIntervalsRef = useRef<{[key: number]: NodeJS.Timeout}>({}); ``` **修复后**(正确): ```typescript const StreamerConsole = () => { // ✅ 所有Hook必须在组件顶部定义 const { user } = useAuth(); const navigate = useNavigate(); // 基础状态Hook const [myChests, setMyChests] = useState([]); const [loading, setLoading] = useState(true); const [showCreateForm, setShowCreateForm] = useState(false); // ... 其他状态Hook // ✅ 倒计时相关Hook也在顶部定义 const [countdowns, setCountdowns] = useState<{[key: number]: number}>({}); const countdownIntervalsRef = useRef<{[key: number]: number}>({}); // 权限检查(不影响Hook调用) useEffect(() => { if (!user) { navigate('/login'); return; } loadMyChests(); }, [user, navigate]); // 加载状态检查(不影响Hook调用) if (loading) { return ; } // 计算逻辑(不在Hook中) const activeChests = myChests.filter(c => c.status === 0 || c.status === 1); ``` ### 2. 修复类型定义 **修复前**: ```typescript // 前端环境中NodeJS.Timeout不存在 const intervalRef = useRef(null); ``` **修复后**: ```typescript // 浏览器环境使用number类型 const intervalRef = useRef(null); ``` ### 3. 清理未使用代码 删除了未使用的`stopCountdown`函数,避免TypeScript警告。 ## 修复结果 ### ✅ 验证测试 1. **TypeScript编译**: ✅ 通过 ```bash npm run build # 之前的Hook错误已消失 ``` 2. **开发服务器启动**: ✅ 成功 ``` VITE v5.4.21 ready in 340 ms ➜ Local: http://localhost:3001/ ``` 3. **页面渲染**: ✅ 正常 - 不再出现空白页面 - 倒计时功能正常工作 - 控制台无错误 ## 技术要点 ### React Hooks规则 1. **只在最顶层调用Hook** - 不要在循环、条件或嵌套函数中调用 - 只在React函数中调用 2. **Hook顺序必须一致** - 每次渲染时Hook的调用顺序必须相同 - 条件渲染后不能定义新Hook 3. **类型定义注意事项** - 前端环境(浏览器)使用DOM API类型 - `NodeJS.Timeout`是Node.js类型,不适用于浏览器 - 浏览器中使用`number`类型表示定时器ID ## 最佳实践 ### 1. Hook组织结构 ```typescript const Component = () => { // 1. 基础Hook(useState, useRef等) const [state1, setState1] = useState(); const [state2, setState2] = useState(); const ref = useRef(); // 2. 计算Hook(useMemo, useCallback) const computed = useMemo(() => ..., []); // 3. 副作用Hook(useEffect) useEffect(() => { // 副作用逻辑 }, []); // 4. 条件返回(不影响Hook调用) if (loading) { return ; } // 5. 渲染逻辑 return
{computed}
; }; ``` ### 2. 类型安全 ```typescript // ✅ 正确:浏览器环境 const timerRef = useRef(null); // ❌ 错误:Node.js类型 const timerRef = useRef(null); ``` ## 预防措施 1. **代码审查清单** - [ ] 所有Hook在组件顶部定义 - [ ] 没有Hook在条件渲染后定义 - [ ] 使用正确的类型定义 2. **IDE配置** - 启用TypeScript严格模式 - 配置ESLint规则检查Hook使用 3. **测试覆盖** - 单元测试验证组件渲染 - 集成测试验证用户交互 ## 总结 本次修复解决了React Hooks使用错误导致的页面崩溃问题。通过正确组织Hook声明顺序和修复类型定义,确保了: - ✅ 页面正常渲染,无空白页 - ✅ 倒计时功能正常工作 - ✅ 无TypeScript类型错误 - ✅ 代码符合React最佳实践 **修复文件**: - `frontend/src/pages/StreamerConsole.tsx` - 修复Hook顺序 - `frontend/src/components/ChestCard.tsx` - 修复类型定义 - `frontend/src/services/websocket.ts` - 修复类型定义 **修复时间**: 2025-12-15 **修复状态**: ✅ 完成 **测试状态**: ✅ 通过