72 lines
1.3 KiB
Markdown
72 lines
1.3 KiB
Markdown
# 🔧 Hydration Warning Fix
|
|
|
|
## ❌ Warning:
|
|
```
|
|
Warning: Extra attributes from the server: class
|
|
at html/body tags
|
|
```
|
|
|
|
## ✅ Nguyên nhân:
|
|
- Server render HTML với một class
|
|
- Client (localStorage) có thể có theme khác
|
|
- → Class mismatch → Hydration warning
|
|
|
|
## 🛠️ Giải pháp:
|
|
|
|
### 1. **Blocking Script trong `<head>`**
|
|
```typescript
|
|
<head>
|
|
<script dangerouslySetInnerHTML={{
|
|
__html: `
|
|
// Chạy ĐỒNG BỘ trước khi React hydrate
|
|
const theme = localStorage.getItem('theme') || 'dark';
|
|
document.documentElement.className = 'h-full' + (theme === 'dark' ? ' dark' : '');
|
|
`
|
|
}} />
|
|
</head>
|
|
```
|
|
|
|
### 2. **suppressHydrationWarning**
|
|
```typescript
|
|
<html suppressHydrationWarning>
|
|
<body suppressHydrationWarning>
|
|
```
|
|
|
|
### 3. **ThemeProvider không render div wrapper**
|
|
```typescript
|
|
// ThemeContext.tsx - chỉ return children
|
|
<ThemeContext.Provider value={contextValue}>
|
|
{children}
|
|
</ThemeContext.Provider>
|
|
```
|
|
|
|
## ✅ Kết quả:
|
|
```
|
|
✅ No hydration warnings
|
|
✅ No flash of incorrect theme
|
|
✅ Theme switching works perfectly
|
|
```
|
|
|
|
## 📊 Flow:
|
|
|
|
```
|
|
Server Render
|
|
↓
|
|
<html> (no class)
|
|
↓
|
|
Blocking Script (in <head>)
|
|
↓
|
|
Read localStorage → Set className
|
|
↓
|
|
React Hydration
|
|
↓
|
|
Sees correct class → No warning ✅
|
|
↓
|
|
ThemeProvider manages theme after mount
|
|
```
|
|
|
|
---
|
|
|
|
**🎉 Hydration warning FIXED!**
|
|
|