Skip to content

Tại sao bạn nên bật "--react-compiler" trong dự án Next.js mới nhất?

Lợi ích rõ ràng nhất: Xóa sổ useMemo, useCallback, memo.
Bạn đang xây dựng một component danh sách sản phẩm. Ban đầu nó đơn giản, nhưng dần dần bạn thêm filter, sort, pagination… và bắt đầu nhận ra UI bị giật khi user tương tác.
Bạn mở DevTools, thấy cả cây component re-render mỗi khi state thay đổi dù phần lớn trong số đó không liên quan gì đến thay đổi đó.
Giải pháp quen thuộc là bọc mọi thứ trong useMemo, useCallback, memo
Nhưng rồi bạn lại phải lo thêm: dependency array có đúng không? Có vô tình tạo closure stale không? Code ngày càng khó đọc hơn.
Đó chính xác là vấn đề mà React Compiler sinh ra để giải quyết.

React Compiler là gì?

React Compiler là một build-time tool — nghĩa là nó chạy trong quá trình build, phân tích code của bạn và tự động thêm memoization vào những chỗ cần thiết. Bạn không cần sửa một dòng code nào.
Tháng 10/2025, React Compiler chính thức ra mắt phiên bản stable 1.0, sau nhiều năm được phát triển nội bộ tại Meta (trước đây có tên là “React Forget”). Hiện tại nó hỗ trợ:
React 17, 18, 19 (tối ưu nhất với React 19)
Next.js 15.3.1+ — tích hợp sẵn, bật bằng một dòng config
Next.js 16 — React Compiler Support được đánh dấu là stable
React Native / Expo
Vite, Rsbuild, và các bundler khác

Lợi ích rõ ràng nhất: Xóa sổ useMemo, useCallback, memo

Đây là ví dụ điển hình nhất. Trước khi có React Compiler, bạn phải viết:
// ❌ Trước — manual memoization, dài dòng và dễ sai
import { useMemo, useCallback, memo } from 'react'

const ProductList = memo(function ProductList({ products, onSelect }) {
const sorted = useMemo(() => {
return [...products].sort((a, b) => a.price - b.price)
}, [products])

const handleSelect = useCallback((id: string) => {
onSelect(id)
}, [onSelect])

return (
<ul>
{sorted.map(p => (
<ProductItem key={p.id} product={p} onClick={() => handleSelect(p.id)} />
))}
</ul>
)
})
Có một bug tinh vi ở đây: dù handleSelect được bọc trong useCallback, arrow function () => handleSelect(p.id) vẫn tạo ra một function mới mỗi lần render — khiến ProductItem luôn re-render bất kể memo có được dùng hay không.
Với React Compiler, bạn chỉ cần viết:
// ✅ Sau — clean, đơn giản, compiler lo phần còn lại
function ProductList({ products, onSelect }) {
const sorted = [...products].sort((a, b) => a.price - b.price)

const handleSelect = (id: string) => {
onSelect(id)
}

return (
<ul>
{sorted.map(p => (
<ProductItem key={p.id} product={p} onClick={() => handleSelect(p.id)} />
))}
</ul>
)
}
Compiler sẽ tự phân tích data-flow, xác định những giá trị nào thực sự thay đổi và memoize chính xác từng phần — kể cả từng JSX element riêng lẻ, không phải chỉ toàn bộ component.

Compiler đã làm gì đằng sau hậu trường?

React Compiler hoạt động theo các bước sau ở build time:
Phân tích AST — Compiler đọc code của bạn và xây dựng một biểu diễn trung gian (HIR — High-level Intermediate Representation) riêng của nó.
Hiểu data-flow và mutability — Compiler theo dõi từng biến, prop, state để biết cái nào có thể thay đổi giữa các lần render.
Tự động thêm memoization — Compiler chèn memoization vào đúng chỗ, thậm chí có thể memoize có điều kiện — điều mà useMemo/useCallback thủ công không thể làm được.
Bạn có thể quan sát điều này trong React DevTools: các component được compiler tối ưu sẽ hiển thị badge “Memo ✨”.

Điều kiện bắt buộc: Tuân thủ Rules of React

React Compiler không phải phép màu tùy tiện. Nó hoạt động dựa trên giả định rằng code của bạn tuân theo . Nếu không, compiler sẽ bỏ qua component đó và không tối ưu — hoặc tệ hơn, tối ưu sai.
Có ba nhóm rules chính bạn cần nắm:

1. Components và Hooks phải pure

Xem chi tiết:
Component phải idempotent — cùng props/state thì luôn trả về cùng output.
Side effects (gọi API, đọc/ghi file…) không được chạy trong render, chỉ chạy trong useEffect hoặc event handler.
Props và state là immutable — không được mutate trực tiếp, phải tạo object mới.

2. React gọi Components và Hooks, không phải bạn

Xem chi tiết:
Không gọi component như một function thông thường — chỉ dùng chúng trong JSX.
Không truyền hook như một giá trị thông thường — hook chỉ được gọi bên trong component.

3. Rules of Hooks

Xem chi tiết:
Chỉ gọi hook ở top level — không gọi trong vòng lặp, điều kiện, hoặc hàm lồng nhau.
Chỉ gọi hook trong React function — không gọi trong function JavaScript thông thường.

Dùng ESLint để phát hiện code không tương thích

Trước khi bật React Compiler cho toàn bộ dự án, bạn nên kiểm tra xem code hiện tại có vi phạm Rules of React không. Công cụ để làm điều này là eslint-plugin-react-hooks — từ phiên bản stable 1.0, các compiler rules đã được tích hợp thẳng vào plugin này.

Cài đặt

npm install --save-dev eslint-plugin-react-hooks@latest

Cấu hình trong eslint.config.mjs

import reactHooks from 'eslint-plugin-react-hooks'

export default [
{
plugins: {
'react-hooks': reactHooks,
},
rules: {
...reactHooks.configs['recommended-latest'].rules,
},
},
]
Preset recommended-latest bao gồm đầy đủ các compiler rules mới nhất, bao gồm:
Phát hiện setState trong render gây render loop
Phát hiện side effects nặng bên trong useEffect
Phát hiện truy cập ref không an toàn trong render

Chạy lint

npx eslint src/
Nếu có lỗi, ESLint sẽ chỉ ra đúng file và dòng code vi phạm. Hãy sửa hết trước khi bật compiler.
Lưu ý: ESLint plugin có thể cài và chạy độc lập, không cần bật React Compiler. Đây là cách an toàn để kiểm tra chất lượng code trước.

Bật React Compiler trong Next.js

Sau khi lint sạch, bật compiler chỉ cần một bước:
npm install babel-plugin-react-compiler@latest
// next.config.ts
Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.