React 19 Compiler: Auto-Memoization và Actions Hooks giúp tăng performance cho web app

5 tháng trước · 10 phút đọc
React Compiler là gì và tại sao bạn nên quan tâm ngay bây giờ
Bạn có bao giờ nhìn vào một component React và thấy mình đang viết useMemo, useCallback, React.memo nhiều hơn cả logic thực sự không? Mình từng có một file component dài 300 dòng mà gần 80 dòng là các wrapper memo. Không phải vì app phức tạp - mà vì mình sợ re-render.
React Compiler (trước đây có tên React Forget) ra mắt stable với React 19 giải quyết đúng vấn đề đó. Thay vì bạn phải tự identify và annotate từng chỗ cần memoize, compiler phân tích toàn bộ codebase tại build-time, tự động inject memoization ở đúng chỗ cần thiết.
Compiler làm việc ở build-time - bạn viết code bình thường, nó lo phần optimize
Nó được release stable từ tháng 10/2025 dưới dạng Babel plugin babel-plugin-react-compiler, là opt-in (không bật mặc định). Bạn vẫn giữ toàn quyền kiểm soát.
Điểm khác biệt quan trọng so với memoization thủ công: compiler hoạt động ở granular level, tức là prop-level thay vì toàn bộ component. Nó chỉ skip re-render cho phần nào thực sự không thay đổi, không phải block cả component như React.memo truyền thống.
Cơ chế hoạt động: 3 phase build-time analysis
Compiler không phải magic - nó chạy 3 bước phân tích tại build-time trước khi bundle code của bạn.
Phase 1 - Analysis: Parse AST (cây cú pháp), theo dõi data-flow và mutability của từng biến, prop, state.
Phase 2 - Inference: Phân loại values thành reactive (props/state thay đổi theo user interaction) và static (hằng số, pure functions không phụ thuộc state).
Phase 3 - Transformation: Inject memoization boundaries tại đúng vị trí. Không memoize những gì rẻ để tính toán lại.
3 phase phân tích - memoization chỉ được inject khi thật sự cần thiết
Một ví dụ thực tế. Bạn có component này:
Compiler sẽ phân tích và nhận ra theme và user là props reactive. Nó sẽ inject equality checks ở prop-level - nếu user.name không đổi nhưng parent re-render vì lý do khác, component này sẽ skip.
Một điểm mình thấy thú vị hơn cả: conditional memoization. Manual React.memo không hoạt động tốt với early returns (vì nó wrap toàn bộ component output). Compiler thì xử lý được - memoize ngay cả khi component có early return ở giữa.
Nhưng có những pattern sẽ block compiler - không auto-memo được:
- Mutate props hoặc refs trong render
- Dùng
Math.random(),Date.now()trực tiếp trong render - Close over mutable variables từ bên ngoài component
Nhớ nguyên tắc này: nếu component của bạn không pure thì compiler sẽ skip nó. Đây thật ra là lý do tốt để refactor code cũ.
Actions hooks trong React 19: useActionState, useFormStatus, useOptimistic
Bên cạnh auto-memoization, React 19 giới thiệu một nhóm hooks mới gọi là Actions - thay đổi cách bạn xử lý forms và async mutations, đặc biệt khi kết hợp Next.js 15 Server Actions.
Vấn đề với React 18 và forms: bạn cần tự manage loading state, error state, optimistic updates - mỗi thứ một useState, một useEffect, và cả đống re-renders không cần thiết.
Actions hooks gom logic form vào 1 chỗ - code ít hơn, re-render ít hơn
useActionState là hook đáng dùng nhất trong bộ này:
So sánh với cách React 18: bạn sẽ cần ít nhất 3-4 useState để làm điều tương tự, cộng thêm một useCallback để wrap handler, cộng thêm potential re-render bugs.
useOptimistic giải quyết UX cho trường hợp bạn cần update UI ngay lập tức mà không chờ server:
Kết hợp compiler + actions hooks, re-renders trong form flow giảm đáng kể. Compiler tự memo các phần static của UI, actions hooks gom async logic vào một chỗ có internal batching.
Benchmark thực tế: compiler vs manual useMemo
Claim "tăng performance 3x" xuất hiện nhiều trong các bài marketing. Thực tế thì phức tạp hơn - số này phụ thuộc rất nhiều vào loại app và pattern cũ của bạn.
Dựa trên các benchmark được ghi nhận, list apps và table với cascading re-renders là nơi compiler thể hiện rõ nhất: 60% reduction in re-render count (dev.to React Compiler Deep Dive).
Benchmark phụ thuộc heavily vào app - đừng tin số marketing, tự đo với DevTools
Mình tổng hợp so sánh thực dụng:
| Tiêu chí | Manual useMemo/React.memo | React Compiler |
|---|---|---|
| Identify điểm cần memo | Thủ công, dễ bỏ sót | Tự động build-time |
| Conditional memo (early return) | Không hỗ trợ tốt | Hỗ trợ tốt |
| Over-memoization | Rủi ro cao nếu không cẩn thận | Heuristics skip cheap renders |
| Granularity | Component-level | Prop-level |
| Debug khi lỗi | Tường minh trong code | Cần DevTools plugin |
| Custom equality logic | Hỗ trợ (tham số thứ 2) | Không tùy chỉnh được |
| External libs (Zustand, Redux) | Tốt | Cần kiểm tra compatibility |
Điểm compiler không thay thế được manual optimization: khi bạn cần custom equality function phức tạp, hoặc khi integrate với state management bên ngoài dùng mutable stores.
React DevTools hiện có plugin hỗ trợ visualize compiler output - bật lên để xem component nào đang được auto-memo, component nào bị skip.
Takeaway thực tế: nếu app của bạn có cascading re-renders (parent re-render kéo theo chuỗi child re-render không cần thiết), compiler giải quyết được hầu hết mà không cần bạn touch code. Nếu bottleneck đến từ heavy computation trong render (ví dụ filter 10,000 records), bạn vẫn cần useMemo thủ công vì đó là logic business, không phải vấn đề re-render.
Migrate từ React 18 lên React 19 + Compiler: từng bước
Migration không phải flip một switch. Mình đã thử trên một project Next.js 14 → 15 thực tế và có vài điểm cần lưu ý.
Bước 1: Upgrade dependencies
Bước 2: Enable compiler trong config
Nếu dùng Next.js 15, thêm vào next.config.js:
Hoặc nếu dùng Babel trực tiếp:
Migrate từng bước - đừng xóa useMemo cũ ngay, đo trước rồi quyết định sau
Bước 3: ĐỪNG xóa useMemo cũ ngay
Đây là sai lầm phổ biến nhất. Compiler và manual memo có thể cùng tồn tại. Xóa hết một lúc có thể tạo ra behavior thay đổi khó debug. Quy trình đúng:
- Enable compiler, giữ nguyên code cũ
- Chạy test suite đầy đủ
- Profile với React DevTools trước/sau
- Dần dần remove manual memo ở những chỗ đã verify compiler cover được
Bước 4: Fix các patterns block compiler
Chạy eslint với rule của compiler để detect tự động:
Plugin này sẽ flag những pattern khiến compiler skip component của bạn - props mutation, non-deterministic values trong render, v.v.
Breaking changes cần biết: Rules of Hooks vẫn được enforce nghiêm hơn. Một số pattern "trốn" được ở React 18 sẽ throw error ở React 19. Thường gặp nhất là conditional hook calls và hooks trong loops.
Nếu bạn chưa familiar với React cơ bản, khóa Xây Dựng Website với ReactJS của F8 là chỗ tốt để nắm foundation trước khi nhảy vào các tính năng advanced này.
Best practices cho production app Việt Nam
Một vài điểm thực tế khi apply React 19 Compiler cho app production, đặc biệt các use case phổ biến trong thị trường Việt Nam: e-commerce, dashboard quản lý, app đặt hàng.
1. Kết hợp Server Components + Client Components hợp lý
Next.js 15 + React 19 cho phép bạn push phần lớn rendering lên server. Compiler tối ưu tốt nhất cho client components có state - server components không có state nên compiler không áp dụng nhiều ở đó.
2. Với large lists - profile trước, optimize sau
App quản lý kho, báo cáo bán hàng, hay danh sách đơn hàng thường có lists lớn. Compiler giảm re-renders của parent, nhưng nếu bạn render 5,000 rows không có virtualization thì vẫn lag. Kết hợp compiler với react-window hoặc TanStack Virtual.
Compiler giảm re-renders, virtualization giảm DOM nodes - 2 layer tối ưu khác nhau
3. Forms với useActionState trong app e-commerce
Checkout flow, form đặt hàng, form đăng nhập - tất cả benefit từ useActionState + Server Actions. Loading state tự quản lý, không cần thêm state riêng, compiler memo phần UI không liên quan đến form state.
4. Avoid những anti-pattern này
5. Monitoring sau khi deploy
Bật React DevTools Profiler trên staging, đo baseline trước khi push compiler lên production. Một số third-party components (charting libs, map libs) có thể có side effects trong render khiến compiler skip - cần wrap thêm.
Tóm lại:
- Default: tin tưởng compiler, không cần thêm memo thủ công
- Exception: custom equality logic, external mutable stores, heavy computation business logic
- Action: bật eslint plugin, profile DevTools, fix patterns block compiler
Drop comment bên dưới nếu bạn gặp edge case nào khi migrate - mình sẽ cập nhật bài theo feedback thực tế.
