React Compiler چیست؟ آیا دیگر به useMemo و useCallback نیاز نداریم؟
React Compiler ابزاری رسمی برای بهینهسازی خودکار اپلیکیشنهای React است. در این مقاله با نحوه عملکرد آن، Automatic Memoization، تفاوتش با useMemo و useCallback، راهاندازی در Vite و Next.js و محدودیتهای آن آشنا میشویم.
یکی از چالشهای همیشگی توسعهدهندگان React، مدیریت رندرهای مجدد (Re-renders) و جلوگیری از محاسبات غیرضروری در کامپوننتهاست.
برای حل این مسئله، سالها از ابزارهایی مانند useMemo، useCallback و React.memo استفاده کردهایم. این ابزارها قدرتمند هستند، اما استفاده نادرست یا بیشازحد از آنها میتواند کد را پیچیدهتر و نگهداری آن را دشوارتر کند.
React Compiler با رویکردی متفاوت وارد شده است: بهجای اینکه توسعهدهنده بهصورت دستی مشخص کند چه چیزی باید Memoize شود، Compiler کد را تحلیل میکند و بسیاری از بهینهسازیها را بهصورت خودکار انجام میدهد.
React Compiler در اکتبر ۲۰۲۵ به نسخه پایدار 1.0 رسید و اکنون میتوان از آن در پروژههای واقعی استفاده کرد.
در این مقاله بررسی میکنیم که React Compiler دقیقاً چیست، چگونه کار میکند، چه تفاوتی با Memoization دستی دارد و آیا واقعاً میتوانیم استفاده از Hookهای مربوط به Memoization را کنار بگذاریم یا نه.
React Compiler یک ابزار بهینهسازی در زمان Build است که کد کامپوننتها و Hookهای React را تحلیل میکند و بهصورت خودکار Memoization را در بخشهای قابلبهینهسازی اعمال میکند.
به زبان ساده، Compiler تلاش میکند از اجرای دوباره محاسبات و رندرهایی که نتیجه جدیدی تولید نمیکنند جلوگیری کند؛ بدون اینکه مجبور باشیم برای هر کامپوننت از memo یا برای هر مقدار و تابع از Hookهای Memoization استفاده کنیم.
توسعهدهنده فرانتاند با بیش از 3 سال تجربه در ساخت وبسایتها و اپلیکیشنهای وب مدرن و واکنشگرا، با تمرکز بر React، Next.js، بهینهسازی عملکرد و تجربه کاربری.
برای فهم عملکرد React Compiler ابتدا باید با مفهوم Memoization آشنا باشیم.
Memoization تکنیکی برای ذخیره نتیجه یک محاسبه یا ارجاع یک مقدار است تا در صورت ثابت ماندن ورودیهای مرتبط، از محاسبه یا تولید مجدد آن جلوگیری شود.
React Compiler این کار را در زمان Build انجام میدهد.
در سطح فنی، Compiler کد را تحلیل میکند، جریان داده و تغییرپذیری مقادیر را بررسی میکند و سپس برای بخشهایی که امکان بهینهسازی دارند، منطق Memoization تولید میکند.
این فرآیند میتواند شامل موارد زیر باشد:
جلوگیری از رندر مجدد غیرضروری کامپوننتهای فرزند
Cache کردن مقادیر محاسبهشده
حفظ ارجاع توابع و مقادیر در شرایط مناسب
تشخیص وابستگیهای واقعی در کد
بهینهسازی بخشهای مختلف یک کامپوننت، نه صرفاً کل کامپوننت
یکی از تفاوتهای مهم آن با Memoization دستی این است که Compiler میتواند بخشهای مختلف یک تابع را بهصورت دقیقتر و حتی شرطی بهینه کند.
زمانی که به کنترل دقیقتری روی Cache شدن یک مقدار یا تابع نیاز داریم.
زمانی که پایداری یک مقدار برای Dependencyهای یک Effect اهمیت دارد.
زمانی که پروژه هنوز Compiler را فعال نکرده است.
زمانی که در حال نگهداری کد قدیمی هستیم و حذف Memoization موجود بدون تست میتواند رفتار یا خروجی بهینهسازی را تغییر دهد.
توصیه مهم: در کدهای جدید ابتدا به Compiler اعتماد کنید و فقط در مواردی که به کنترل دقیقتری نیاز دارید از useMemo و useCallback استفاده کنید. در پروژههای موجود نیز قبل از حذف Memoizationهای قبلی، رفتار و Performance را آزمایش کنید.
React Compiler با React 19 بهترین تجربه را ارائه میدهد، اما از React 17 و 18 نیز پشتیبانی میکند؛ البته ممکن است برای نسخههای قدیمیتر به تنظیمات و Runtime مناسب نیاز داشته باشید.
برای راهاندازی، ابتدا پکیج Compiler را نصب میکنیم:
در نسخههای جدید @vitejs/plugin-react، مستندات رسمی React استفاده از reactCompilerPreset را همراه با @rolldown/plugin-babel نشان میدهد.
ابتدا پکیج موردنیاز را نصب کنید:
npm install -D @rolldown/plugin-babel
سپس فایل vite.config.ts را به شکل زیر تنظیم کنید:
import { defineConfig } from "vite";
import react, {
reactCompilerPreset,
} from "@vitejs/plugin-react";
import babel from "@rolldown/plugin-babel";
export default defineConfig({
plugins: [
react(),
babel({
presets: [reactCompilerPreset()],
}),
],
});
توجه کنید که این روش مربوط به نسخههای جدید پلاگین React در Vite است. در نسخههای قدیمیتر، تنظیمات Babel متفاوت است؛ بنابراین بهتر است نسخه پکیجهای پروژه را با مستندات رسمی تطبیق دهید.
در Next.js 16، پشتیبانی از React Compiler پایدار شده و دیگر به تنظیمات Experimental نیاز نداریم.
ابتدا پکیج را نصب کنید:
npm install -D babel-plugin-react-compiler
سپس در next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactCompiler: true,
};
export default nextConfig;
با همین تنظیم، Next.js Compiler را در Build فعال میکند.
در نسخههای قبلی Next.js ممکن است تنظیمات یا وضعیت پشتیبانی متفاوت باشد؛ برای نمونه مستندات Next.js پشتیبانی از این قابلیت را از نسخه 15.3.1 نیز توضیح دادهاند.
اگر یک کامپوننت بیش از حد بزرگ باشد، State در سطح نامناسبی نگهداری شود یا دادههای غیرضروری وارد درخت کامپوننت شوند، Compiler لزوماً مشکل معماری را حل نمیکند.
خیر. میزان بهبود به ساختار پروژه، تعداد رندرهای غیرضروری، محاسبات سنگین و الگوی استفاده از React بستگی دارد.
اگر اپلیکیشن شما از قبل بهخوبی بهینه شده باشد، ممکن است تفاوت چشمگیری مشاهده نکنید.
از طرف دیگر، در پروژههایی که Memoization دستی زیادی دارند یا کامپوننتهای فرزند زیادی بهصورت غیرضروری رندر میشوند، Compiler میتواند تأثیر قابلتوجهی داشته باشد.
برای ارزیابی واقعی، قبل و بعد از فعالسازی Compiler را با شرایط یکسان اندازهگیری کنید.
React Compiler یکی از مهمترین تغییرات در تجربه توسعه React است. این ابزار تلاش میکند بخش زیادی از پیچیدگی Memoization دستی را از دوش توسعهدهندگان بردارد و به آنها اجازه دهد کد سادهتر و خواناتری بنویسند.
بااینحال، Compiler جایگزین درک عمیق React، طراحی صحیح Componentها، مدیریت درست State و اندازهگیری Performance نیست.
پیشنهاد من این است:
در پروژههای جدید React 19، فعالسازی Compiler را جدی بررسی کنید.
در پروژههای موجود، از Adoption تدریجی استفاده کنید.
Memoizationهای قبلی را بدون تست حذف نکنید.
برای بررسی نتایج واقعی، از Profiler و ابزارهای Performance استفاده کنید.
در نهایت، هدف این نیست که صرفاً تعداد useMemo و useCallbackهای پروژه را به صفر برسانیم؛ هدف این است که با کد سادهتر، عملکرد بهتر و پیچیدگی نگهداری کمتر به دست آوریم.