React 19 có gì mới? Server Components, hooks mới và cách tối ưu app web 2026

2 tháng trước · 9 phút đọc
Tại sao React 19 khác những lần nâng cấp trước?
Mình nhớ hồi React 16 ra đời với Hooks - cảm giác như cả cộng đồng dừng lại để học lại từ đầu. React 19 không gây ra cơn địa chấn kiểu đó, nhưng mình nghĩ nó quan trọng hơn nhiều người đang nghĩ.
Lý do? React 18 vẫn cơ bản là client-side library với một số tính năng concurrent. React 19 thì khác - đây là bước chuyển rõ ràng sang mô hình server-first, action-driven. Server Components và Server Functions đã chính thức ổn định, không còn là experimental nữa. Actions được chuẩn hóa thành pattern chính thức cho mọi thao tác bất đồng bộ. React Compiler bắt đầu trưởng thành.
React 19 đánh dấu bước chuyển từ client-first sang server-first rendering
Nếu bạn đang build web app trên Next.js, Remix, hay bất kỳ meta-framework nào hỗ trợ RSC - đây là thời điểm cần hiểu rõ hướng đi mới. Bài này mình sẽ đi qua từng thay đổi quan trọng, kèm code thực tế và quan điểm cá nhân về khi nào nên (và không nên) áp dụng.
Server Components vs Client Components: quy tắc thực dụng
Đây là phần mà mình thấy nhiều bạn vẫn còn mơ hồ nhất. Không phải vì khái niệm khó, mà vì không có ranh giới rõ ràng trong code - bạn phải tự biết mình đang ở layer nào.
Quy tắc đơn giản nhất: mặc định để Server Component, chỉ chuyển sang Client Component khi thật sự cần interactivity phía browser.
Ranh giới server/client không phải về performance đơn thuần - là về trách nhiệm của từng layer
Cụ thể hơn:
Dùng Server Component khi:
- Fetch dữ liệu từ database, CMS, API nội bộ
- Đọc filesystem, environment variables, secrets
- Render layout, marketing pages, content-heavy pages
- Muốn giảm JavaScript bundle gửi xuống client
Chuyển sang Client Component khi:
- Component cần
useState,useEffect, event handlers - Cần đọc
window,localStorage, media query - Drag-and-drop, modal, tabs, rich text editor
- Animation phụ thuộc vào tương tác người dùng
Một mistake mình thấy thường xuyên: bọc cả page trong 'use client' vì có một button tương tác. Điều này phủ nhận toàn bộ lợi ích của Server Components. Cách đúng là tách button thành Client Component riêng, còn phần còn lại của trang vẫn là Server Component.
Actions và form hooks: trước/sau rõ nhất trong React 19
Đây là thay đổi mình thấy giá trị nhất cho developer hằng ngày. Nếu bạn đã từng tự viết loading state, error state, reset form sau submit - bạn sẽ biết nó tẻ nhạt thế nào.
React 19 chuẩn hóa pattern này qua ba hook: useActionState, useFormStatus, và useOptimistic.
Trước React 19
Sau React 19
useFormStatus phải nằm trong component con, không gọi trực tiếp ở form cha
Một điểm cần chú ý: useFormStatus đọc trạng thái của form cha gần nhất trong DOM. Nếu bạn gọi hook này trực tiếp trong component chứa <form>, nó sẽ không hoạt động như mong đợi. Phải tách submit button thành component riêng nằm bên trong <form> - như ví dụ SubmitButton ở trên.
Và đừng nhầm useActionState với useFormState - cái sau đã được đổi tên trong bản ổn định. Nếu code của bạn vẫn dùng useFormState, kiểm tra lại docs của version đang dùng.
useOptimistic: UI mượt mà không cần đợi server
Bạn có bao giờ click like trên một post rồi nhìn màn hình đứng im chờ server phản hồi? Trải nghiệm đó tệ hơn nhiều so với ngay lập tức thấy số like tăng lên - dù thật ra server vẫn đang xử lý.
useOptimistic giải quyết đúng vấn đề này. Hook cho phép hiển thị UI "dự đoán" ngay lập tức, React sẽ tự rollback nếu action thất bại.
Optimistic UI: người dùng thấy kết quả ngay, server xử lý ngầm phía sau
Pattern này đặc biệt hiệu quả cho: like/dislike, checkbox todo, thêm item vào giỏ hàng, gửi tin nhắn chat. Bất kỳ action nào người dùng expect kết quả gần như tức thì.
Trade-off cần biết: nếu server thất bại, UI sẽ rollback về trạng thái trước đó. Điều này tạo ra một "flash" khó chịu nếu lỗi xảy ra thường xuyên. Nên dùng useOptimistic cho các action có xác suất thành công cao, và luôn hiển thị error message rõ ràng khi rollback.
React Compiler: bạn có cần xóa useMemo không?
Câu hỏi mình hay thấy trên mạng sau khi React Compiler được công bố: "Giờ tôi có thể xóa hết useMemo và useCallback không?"
Câu trả lời ngắn: chưa nên làm vội.
React Compiler phân tích code của bạn và tự động thêm memoization ở những nơi cần thiết - tương tự như khi bạn tự viết useMemo, nhưng compiler làm thay. Mục tiêu là giảm "optimization noise" trong codebase: developer viết code logic, compiler lo performance.
React Compiler tự động hóa memoization - nhưng cần rollout dần, không phải bật cả codebase ngay
Lợi ích thực tế cho team lớn:
- Code dễ đọc hơn, ít wrapping trong
useCallback - Ít bug do memoization dependency array sai
- Junior dev không cần hiểu sâu về memoization để viết code performant
Nhưng có một số điều cần thực tế:
1. Rollout dần, đừng bật toàn bộ codebase ngay. Compiler có thể handle 90% trường hợp, nhưng 10% còn lại - thường là những pattern phức tạp hoặc side effects không rõ ràng - có thể gây ra behavior bất ngờ.
2. Đo trước khi xóa. Mình khuyên bạn chạy React DevTools Profiler để xem render count trước và sau khi bật compiler trên một route cụ thể. Chỉ xóa manual memoization khi bạn đã xác nhận compiler đang xử lý đúng.
3. Compiler không thay thế tư duy. Bạn vẫn cần hiểu khi nào computation đắt, khi nào nên tách component, khi nào dependency thay đổi không cần thiết. Compiler hỗ trợ, không thay thế.
Cách bật compiler trong Next.js 15:
Sau khi bật, chạy profiling và so sánh kỹ trước khi remove useMemo / useCallback có sẵn.
Setup Next.js 15 với React 19
Nếu bạn đang bắt đầu dự án mới, đây là cách mình khuyến nghị setup. Với dự án hiện có, phần migrate bên dưới sẽ phù hợp hơn.
Tạo project mới
Next.js 15 khi dùng App Router sẽ mặc định hỗ trợ React 19 và Server Components. Bạn không cần cài thêm gì - chỉ cần hiểu rõ convention:
app/
layout.tsx → Server Component (mặc định)
page.tsx → Server Component (mặc định)
components/
server/ → Server Components (không directive)
client/ → Client Components ('use client' ở đầu file)
Cấu hình TypeScript cho Server Actions
App Router: Server Components làm data layer, Client Components chỉ ở layer tương tác
Lưu ý khi kiểm tra compatibility: một số thư viện UI phổ biến (component library, form library) chưa hoàn toàn support React 19. Kiểm tra peerDependencies của từng package trước khi cài. Nếu thấy cảnh báo peer dependency conflict, đọc changelog của library đó - thường họ đã có version mới tương thích.
