Nếu bạn từng làm việc với React hoặc Next.js, có lẽ bạn đã nghe đến thuật ngữ hydration. Nhưng nó thực sự là gì? Tại sao nó quan trọng? Và làm sao để bạn làm việc với nó—hoặc né tránh những rắc rối từ nó?
Bài viết này sẽ giải thích rõ về hydration: nó là gì, hoạt động thế nào trong React và Next.js, và bạn cần biết gì để debug hoặc tối ưu nó.
Hydration là gì?
Hydration là quá trình JavaScript phía client tiếp quản HTML tĩnh được render từ server, biến nó thành một ứng dụng React tương tác hoàn chỉnh trên trình duyệt.
Hãy tưởng tượng quy trình sau:
- Server gửi HTML đến trình duyệt (tốt cho SEO và tốc độ hiển thị ban đầu).
- HTML này bao gồm một React app "placeholder".
- Khi gói JavaScript được tải ở phía client, nó sẽ "hydrate" HTML tĩnh đó—gắn các sự kiện, state và tính tương tác.
Tại sao Hydration lại quan trọng?
🧠 Thân thiện với SEO: SSR (Server-Side Rendering) gửi HTML thực đến các công cụ tìm kiếm.
⚡ Hiệu suất tốt hơn: Rút ngắn thời gian hiển thị đầu tiên (TTFP).
💬 Tính tương tác: Không có hydration, nội dung SSR sẽ không phản hồi lại thao tác người dùng.
Hydration trong React (không dùng Next.js)
React cung cấp API hydrateRoot từ phiên bản React 18 trở lên, dành riêng cho hydration.
Ví dụ: Hydrate ứng dụng React
client.tsx:
import { hydrateRoot } from 'react-dom/client';
import App from './App'; const container = document.getElementById('root');
if (container) { hydrateRoot(container, <App />);
}
index.html
từ server:
<!DOCTYPE html>
<html lang="en"> <head> <title>Hydration Example</title> </head> <body> <div id="root"><!-- React server-rendered HTML here --></div> <script type="module" src="/client.tsx"></script> </body>
</html>
Hydration trong Next.js
Next.js làm cho quá trình hydration gần như "vô hình"—nhưng về bản chất, nó vẫn theo quy trình cũ: render HTML từ server, sau đó hydrate bằng React phía client.
Một trang điển hình trong Next.js (App Router)
app/page.tsx
:
export default function HomePage() { return ( <main> <h1>Hello from Next.js!</h1> <Counter /> </main> );
}
components/Counter.tsx
:
'use client'; import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Count: {count} </button> );
}
Counter
là component phía client ('use client
').
HomePage
được render từ server, sau đó Counter được hydrate ở phía client.
SSR, SSG và Hydration trong Next.js
- SSR (Server-Side Rendering): Dùng
getServerSideProps
- SSG (Static Site Generation): Dùng
getStaticProps
Cả hai đều được hydrate sau khi HTML được gửi tới client.
Hiểu điều này sẽ giúp bạn debug các lỗi như:
- Cảnh báo nội dung không khớp
- Tương tác bị trễ
- Trạng thái không đồng bộ giữa client/server
Các vấn đề thường gặp với Hydration
1. Cảnh báo markup không khớp
React sẽ cảnh báo nếu HTML render từ server khác với render phía client:
Warning: Text content did not match. Server: "Hello" Client: "Hi"
Giải pháp: Đảm bảo nội dung SSR là xác định (deterministic)—không dựa vào window
, Math.random()
, hoặc state cục bộ.
// ❌ This will break hydration
const Greeting = () => <div>{Math.random()}</div>; // ✅ Fix by deferring to client
'use client';
const Greeting = () => { const [num] = useState(() => Math.random()); return <div>{num}</div>;
};
Tránh Hydration không cần thiết
Nếu bạn dùng Next.js và không muốn hydrate một phần nào đó (ví dụ: nội dung tĩnh), tránh dùng 'use client
'.
Hoặc kiểm soát kỹ hơn bằng cách:
import dynamic from 'next/dynamic'; const ClientOnlyComponent = dynamic(() => import('./ClientComp'), { ssr: false,
});
→ Component này chỉ được tải ở phía client và bỏ qua SSR.
Tối ưu hóa Hydration
- Tách gói với dynamic import
- Giảm số lượng component phía client
- Di chuyển UI tĩnh ra khỏi client component
- Tránh tính toán nặng khi render
Từ Next.js 13+ trở đi, App Router khuyến khích dùng server components mặc định → giảm chi phí hydration.
Debug Hydration
Dùng React DevTools và console trình duyệt để:
- Kiểm tra khi component được mount
- Phát hiện sự khác biệt giữa client/server
- Ghi lại hành vi hydration:
useEffect(() => { console.log('Component hydrated on client');
}, []);
Tổng kết
✔ Hydration biến HTML tĩnh thành React component tương tác.
✔ Được xử lý tự động trong React 18+ và Next.js 13+.
✔ Dùng 'use client' để render phía client khi cần.
✔ Tránh dùng nội dung không xác định trong SSR.
✔ Tách component động hoặc trì hoãn hydration khi có thể.
✔ Dùng server component để tối ưu hóa performance.
Kết luận
Hydration là yếu tố cốt lõi giúp xây dựng ứng dụng React/Next.js hiện đại, nhanh, thân thiện với SEO và có tính tương tác cao. Khi hiểu rõ cách hoạt động và cách kiểm soát nó, bạn sẽ phát triển ứng dụng ổn định, dễ mở rộng, ít lỗi và hiệu suất tốt hơn.