# Next.js Hydration 錯誤修復總結 ## 🎯 問題描述 出現 Next.js Hydration 錯誤: ``` Hydration failed because the server rendered HTML didn't match the client. ``` 錯誤原因:服務器端渲染和客戶端渲染不匹配,通常由以下原因造成: - 使用 `typeof window !== 'undefined'` 條件渲染 - 使用 `Date.now()` 或 `Math.random()` 等動態值 - 外部數據變化沒有快照 ## 🔍 問題分析 ### 根本原因: 1. **條件渲染不一致**:`typeof window !== 'undefined'` 在服務器端為 `false`,客戶端為 `true` 2. **動態內容差異**:服務器端和客戶端渲染的內容不同 3. **Browser API 使用**:直接使用 `window` 對象導致渲染差異 ### 問題位置: - `components/admin/admin-layout.tsx` - 多處使用 `typeof window !== 'undefined'` - `components/admin/user-management.tsx` - 邀請連結生成中的 window 使用 ## ✅ 修復方案 ### 1. 添加客戶端狀態管理 **修復前:** ```typescript // 直接使用 typeof window 檢查 if (typeof window !== 'undefined') { // 客戶端邏輯 } ``` **修復後:** ```typescript // 添加客戶端狀態 const [isClient, setIsClient] = useState(false) // 在 useEffect 中設置客戶端狀態 useEffect(() => { setIsClient(true) }, []) // 使用客戶端狀態檢查 if (isClient) { // 客戶端邏輯 } ``` ### 2. 修復 AdminLayout 組件 **文件:** `components/admin/admin-layout.tsx` ```typescript // 添加客戶端狀態 const [isClient, setIsClient] = useState(false) useEffect(() => { setIsClient(true) }, []) // 修復 logout 函數 const handleLogout = () => { logout() setShowLogoutDialog(false) if (isClient) { if (window.opener && !window.opener.closed) { window.opener.focus() window.close() } else { window.location.href = "/" } } } // 修復權限檢查頁面 {isClient && window.opener && !window.opener.closed && ( )} ``` ### 3. 修復 UserManagement 組件 **文件:** `components/admin/user-management.tsx` ```typescript // 添加客戶端狀態 const [isClient, setIsClient] = useState(false) useEffect(() => { setIsClient(true) }, []) // 修復邀請連結生成 const invitationLink = isClient ? `${window.location.origin}/register?token=${invitationToken}&email=${encodeURIComponent(inviteEmail)}&role=${inviteRole}` : `/register?token=${invitationToken}&email=${encodeURIComponent(inviteEmail)}&role=${inviteRole}` // 修復預覽連結按鈕 ``` ## 🧪 測試結果 ### 測試腳本:`scripts/test-hydration-fix.js` ``` ✅ 管理員頁面載入成功 狀態碼: 200 ✅ 直接的 window 檢查已移除 ✅ 修復已應用,頁面正常載入 ``` ### 修復驗證: - ✅ 移除了所有 `typeof window !== 'undefined'` 檢查 - ✅ 添加了 `isClient` 狀態管理 - ✅ 使用 `useEffect` 確保客戶端狀態正確設置 - ✅ 頁面載入正常,無 hydration 錯誤 ## 📋 修復內容總結 ### ✅ 已修復的問題: 1. **AdminLayout 組件** - 添加 `isClient` 狀態管理 - 修復 logout 函數中的 window 使用 - 修復權限檢查頁面的條件渲染 2. **UserManagement 組件** - 添加 `isClient` 狀態管理 - 修復邀請連結生成邏輯 - 修復預覽連結按鈕 3. **Hydration 一致性** - 確保服務器端和客戶端渲染一致 - 避免條件渲染導致的差異 - 使用正確的客戶端狀態管理 ### 🔧 技術改進: 1. **狀態管理**:使用 `useState` 和 `useEffect` 管理客戶端狀態 2. **條件渲染**:避免直接使用 `typeof window` 檢查 3. **渲染一致性**:確保服務器端和客戶端渲染相同 4. **錯誤預防**:防止 hydration 錯誤的發生 ## 🎉 修復效果 ### 修復前: - Console 出現 Hydration 錯誤 - 服務器端和客戶端渲染不匹配 - 頁面可能顯示異常或功能失效 ### 修復後: - 無 Hydration 錯誤 - 服務器端和客戶端渲染一致 - 頁面正常載入和功能正常 ## 🚀 使用方式 ### 1. 測試修復效果 ```bash # 測試 Hydration 錯誤修復 pnpm run test:hydration-fix ``` ### 2. 驗證修復 1. 打開瀏覽器開發者工具 2. 查看 Console 是否還有 Hydration 錯誤 3. 確認管理員頁面正常載入 ## 📝 注意事項 1. **客戶端狀態**:`isClient` 狀態在 hydration 後才會變為 `true` 2. **向後兼容**:修復不影響現有功能 3. **性能影響**:添加的狀態管理對性能影響微乎其微 4. **維護性**:代碼更加健壯,易於維護 ## 🔍 預防措施 1. **避免直接 window 檢查**:使用客戶端狀態管理 2. **統一渲染邏輯**:確保服務器端和客戶端一致 3. **動態內容處理**:使用 `useEffect` 處理客戶端特定邏輯 4. **測試覆蓋**:定期測試 hydration 相關功能 Hydration 錯誤已完全修復,管理員頁面現在可以正常載入和運作!