ویرگول
ورودثبت نام
شراره شادالو
شراره شادالو
شراره شادالو
شراره شادالو
خواندن ۲ دقیقه·۱۷ روز پیش

درک Closure در React

اگر مدتی با React کار کرده باشی، احتمالاً با یکی از این موقعیتها روبهرو شدی:

  • state آپدیت میشود، ولی داخل setInterval مقدار قدیمی چاپ میشود

  • یک event handler انگار «در گذشته گیر کرده»

  • یا لاگها با چیزی که روی UI میبینی نمیخوانند

و معمولاً جواب این است:

«این به خاطر Closure است»

ولی Closure در React دقیقاً یعنی چه؟

Closure یعنی:

یک تابع، متغیرهایی را که موقع ساخته شدنش وجود داشتهاند،حتی بعد از پایان آن scope، در حافظه نگه میدارد.

function outer() { let count = 0 return function inner() { console.log(count) } } const fn = outer() fn() // 0

تابع inner هنوز count را میشناسد،با اینکه outer دیگر اجرا نمیشود.این رفتار اسمش Closure است.

چرا Closure در React مهم میشود؟

چون React اینطوری کار میکند:

با هر render

متغیرها دوباره ساخته میشوند و توابع دوباره ساخته میشوند

اما بعضی توابع قدیمی زنده میمانند(مثل setInterval، event handlerها، effectها)

و اینجا Closure خودش را نشان میدهد.

مثال باگ Closure در React

function Counter() { const [count, setCount] = React.useState(0) React.useEffect(() => { const id = setInterval(() => { console.log('interval sees:', count) }, 1000) return () => clearInterval(id) }, []) return ( <button ={() => setCount(count + 1)}> count: {count} </button> ) }

چه اتفاقی میافتد؟

  • با کلیک، عدد روی صفحه زیاد میشود ✅

  • ولی داخل console، همیشه 0 چاپ میشود ❌

چرا این باگ اتفاق میافتد؟

  • useEffect فقط یکبار اجرا شده

  • تابع داخل setInterval همان موقع ساخته شده

  • آن تابع، count مربوط به رندر اول را در Closure خودش نگه داشته

به زبان ساده:

این interval فقط «گذشته» را میبیند، نه state فعلی را

.Render 1:

count = 0

interval → count = 0 را ذخیره میکند

Render 2:

count = 1

interval هنوز همان closure قدیمی را دارد

Closure خودش آپدیت نمیشود؛باید دوباره ساخته شود.

راهحل اول: اضافه کردن dependency

useEffect(() => { const id = setInterval(() => { console.log(count) }, 1000) return () => clearInterval(id) }, [count])

✔ همیشه مقدار جدید را میبیند
✖ interval در هر تغییر destroy و create میشود

برای بعضی سناریوها خوب است، برای بعضی نه.

راهحل دوم: آپدیت فانکشنی (پیشنهادی)

اگر هدف فقط آپدیت state است:

setCount(prev => prev + 1)

این روش چرا خوبه؟

  • به Closure وابسته نیست

  • React مقدار آخر state را تضمین میکند

  • باگهای Closure را دور میزند

راهحل سوم: useRef (حرفهای)

وقتی میخواهی:

  • effect فقط یکبار اجرا شود

  • ولی همیشه به آخرین مقدار دسترسی داشته باشی

function Counter() { const [count, setCount] = React.useState(0) const countRef = React.useRef(count) React.useEffect(() => { countRef.current = count }, [count]) React.useEffect(() => { const id = setInterval(() => { console.log(countRef.current) }, 1000) return () => clearInterval(id) }, []) return ( <button ={() => setCount(c => c + 1)}> {count} </button> ) }

اینجا:

  • ref با render عوض نمیشود

  • مقدارش همیشه بهروز است

  • interval همیشه مقدار جدید را میخواند

این الگو در پروژههای بزرگ خیلی رایج است

Closure در event handlerها

const handleClick = () => { setTimeout(() => { console.log(value) }, 2000) }

اگر value قبل از ۲ ثانیه تغییر کند:

  • مقدار قدیمی چاپ میشود

دلیل؟
همان Closure.

Closure باگ React نیست ویژگی JavaScript است
React فقط باعث میشود بیشتر دیده شود

جمعبندی نهایی

هر وقت در React دیدی:

  • داده قدیمی است

  • state درست به نظر نمیرسد

  • لاگها عجیباند

از خودت بپرس:

  • این تابع کی ساخته شده؟

  • چه متغیرهایی را در Closure خودش نگه داشته؟

  • به مقدار جدید نیاز دارم یا مقدار زمان ساخت؟

reactjavascriptinterview
۳
۰
شراره شادالو
شراره شادالو
شاید از این پست‌ها خوشتان بیاید