یکی از سوال‌هایی که بعد از کار با React برای خیلی از برنامه نویس‌ها پیش می‌آید این است که چرا بعضی کامپوننت‌ها دوبار اجرا یا Render می‌شوند. این موضوع مخصوصا بعد از React 18 بیشتر دیده شد و باعث شد خیلی از توسعه دهنده‌ها فکر کنند پروژه مشکل دارد. در واقع، دوبار Render شدن React معمولا به خاطر Strict Mode، مدیریت اشتباه State یا استفاده نادرست از useEffect اتفاق می‌افتد و اگر دلیل آن را بدانید، کنترلش خیلی راحت‌تر می‌شود.

Render در React دقیقا چیست؟

هر بار که React رابط کاربری را به روز رسانی می‌کند، فرآیندی به نام Render اتفاق می‌افتد.

به زبان ساده:

  • React داده‌ها را بررسی می‌کند
  • تغییرات را تشخیص می‌دهد
  • UI را دوباره محاسبه می‌کند

مثلا:

function App() {
  return <h1>Hello React</h1>
}

هر بار که State یا Props تغییر کنند، React دوباره این کامپوننت را Render می‌کند.

چرا React دوبار Render می شود؟

دلایل مختلفی وجود دارد اما چند مورد از بقیه رایج‌تر هستند.

فعال بودن React Strict Mode

در React 18 وقتی Strict Mode فعال باشد، React بعضی کامپوننت‌ها را عمدا دوبار اجرا می‌کند.

مثلا:

<React.StrictMode>
  <App />
</React.StrictMode>

این رفتار برای پیدا کردن:

  • Side Effect های خطرناک
  • Memory Leak
  • مشکلات Async

طراحی شده است.

خیلی از برنامه نویس‌ها فکر می‌کنند پروژه خراب شده اما در واقع React دارد رفتار کد را بررسی می‌کند.

اجرای دوباره useEffect

یکی از رایج‌ترین دلایل دوبار Render شدن React مربوط به useEffect است.

مثلا:

useEffect(() => {
  fetchData()
})

در این حالت useEffect بعد از هر Render اجرا می‌شود.

چون Dependency Array تعریف نشده است.

روش درست:

useEffect(() => {
  fetchData()
}, [])

حالا فقط یک بار اجرا می‌شود.

تغییر State داخل Render

این اشتباه خیلی خطرناک است:

function App() {
  const [count, setCount] = useState(0)

  setCount(count + 1)

  return <div>{count}</div>
}

اینجا هر Render باعث تغییر State می‌شود و دوباره Render جدید اتفاق می‌افتد.

نتیجه:

  • Loop بی نهایت
  • مصرف شدید CPU
  • کرش شدن صفحه

تغییر مداوم Props

اگر والد مدام داده جدید ارسال کند، فرزند هم دوباره Render می‌شود.

مثلا:

<Child data={{name: "Ali"}} />

در ظاهر مقدار ثابت است اما در هر Render یک Object جدید ساخته می‌شود.

React این را تغییر جدید در نظر می گیرد.

استفاده اشتباه از Context

بعضی پروژه‌ها تمام State را داخل Context قرار می‌دهند.

نتیجه:

  • با کوچک‌ترین تغییر
  • کل کامپوننت‌ها دوباره Render می‌شوند

این موضوع در پروژه‌های بزرگ Performance را نابود می‌کند.

دوبار Render شدن React در React 18

بعد از React 18 خیلی از توسعه دهنده‌ها متوجه این رفتار شدند.

مخصوصا در Development Mode.

مثلا Console دوبار لاگ می‌دهد:

console.log("render")

خروجی:

render
render

این موضوع معمولا به خاطر Strict Mode است و در Production اتفاق نمی‌افتد.

چه زمانی دوبار Render شدن طبیعی است؟

همیشه دوبار Render شدن مشکل نیست.

بعضی وقت‌ها React برای:

  • بهینه سازی
  • بررسی تغییرات
  • پیدا کردن Side Effect

این کار را انجام می‌دهد.

اگر:

  • پروژه کند نشده
  • Loop ایجاد نشده
  • Memory Leak ندارید

معمولا جای نگرانی نیست.

چه زمانی دوبار Render شدن خطرناک است؟

وقتی:

  • API چند بار صدا زده شود
  • مصرف RAM بالا برود
  • صفحه کند شود
  • درخواست های تکراری ایجاد شوند
  • کامپوننت ها بی دلیل اجرا شوند

باید مشکل را جدی بررسی کنید.

روش پیدا کردن دلیل Render اضافی

استفاده از React DevTools

یکی از بهترین ابزارها React DevTools است.

داخل بخش Profiler می توانید ببینید:

  • کدام کامپوننت Render شده
  • چند بار Render شده
  • چه چیزی باعث Render شده

این ابزار برای Debug پروژه‌های بزرگ خیلی مهم است.

استفاده از console.log

مثلا:

console.log("Component Rendered")

اگر لاگ مدام تکرار شود یعنی Render اضافی دارید.

بررسی useEffect ها

بیشتر Loop ها از useEffect شروع می‌شوند.

مثلا این کد خطرناک است:

useEffect(() => {
  setData(newData)
}, [data])

چون تغییر data دوباره useEffect را اجرا می‌کند.

روش حل دوبار Render شدن React

استفاده درست از Dependency Array

همیشه Dependency ها را دقیق تعریف کنید.

مثلا:

useEffect(() => {
  fetchUsers()
}, [])

این باعث می‌شود Effect فقط یک بار اجرا شود.

استفاده از useMemo

اگر Object یا Array جدید می‌سازید:

const user = useMemo(() => {
  return {name: "Ali"}
}, [])

این کار از Render اضافی جلوگیری می‌کند.

استفاده از React.memo

برای جلوگیری از Render غیر ضروری:

export default React.memo(Component)

این روش مخصوص کامپوننت‌های سنگین خیلی مفید است.

مدیریت درست Context

نباید تمام State پروژه را داخل یک Context قرار دهید.

بهتر است:

  • Context ها کوچک باشند
  • State ها تفکیک شوند
  • داده‌های سنگین جدا مدیریت شوند

حذف Strict Mode برای تست

اگر می خواهید مطمئن شوید مشکل از Strict Mode است:

<App />

موقتا Strict Mode را حذف کنید.

اگر مشکل از بین رفت، یعنی React فقط رفتار پروژه را بررسی می کرده است.

البته در پروژه واقعی بهتر است Strict Mode را نگه دارید.

اشتباهات رایج برنامه نویسان React

صدا زدن API داخل Render

این اشتباه خیلی خطرناک است:

function App() {
  fetch('/api/data')

  return <div>Hello</div>
}

هر Render دوباره API را صدا می‌زند.

ساخت Function جدید در هر Render

مثلا:

<button onClick={() => handleClick()} />

در پروژه‌های بزرگ این موضوع باعث Render اضافی می‌شود.

گاهی بهتر است از useCallback استفاده شود.

استفاده زیاد از Global State

بعضی پروژه‌ها همه چیز را داخل Redux یا Context ذخیره می‌کنند.

نتیجه:

  • Render اضافه
  • مصرف RAM بیشتر
  • کند شدن UI

روش جلوگیری از Render اضافی در React

کامپوننت‌ها را کوچک نگه دارید

کامپوننت‌های بزرگ سخت تر کنترل می‌شوند.

هرچه Component کوچک‌تر باشد:

  • Debug راحت‌تر است
  • Render بهتر مدیریت می‌شود
  • Performance بالاتر می‌رود

State غیر ضروری نسازید

هر State جدید یعنی احتمال Render بیشتر.

قبل از ساخت State از خودتان بپرسید:

  • آیا واقعا لازم است؟
  • آیا می شود Derived Data باشد؟

از Profiler استفاده کنید

خیلی از مشکلات Performance فقط با چشم دیده نمی‌شوند.

Profiler کمک می‌کند دقیقا بفهمید مشکل از کدام بخش پروژه است.

جمع بندی

دوبار Render شدن React همیشه به معنی خراب بودن پروژه نیست. در خیلی از مواقع React برای بررسی Side Effect ها و پیدا کردن مشکلات احتمالی این کار را انجام می‌دهد. اما اگر Render اضافی باعث کندی پروژه، درخواست‌های تکراری یا مصرف زیاد منابع شود، باید ساختار State، useEffect و Context ها را دقیق بررسی کنید. بیشتر مشکلات با مدیریت درست Dependency ها و بهینه سازی کامپوننت‌ها قابل حل هستند.