در درسهای قبل یاد گرفتیم که چطور افکتها را شروع و متوقف کنیم. حالا زمان آن رسیده است که به یک مفهوم بسیار حیاتی در ریآکت مسلط شویم: مقادیر واکنشپذیر یا همان Reactive Values.
وقتی متغیری را درون افکت استفاده میکنید، ریآکت بر اساس «واکنشپذیر» بودن یا نبودن آن متغیر تصمیم میگیرد که آیا باید آن را در آرایه وابستگیها (Dependency Array) بنویسید یا خیر. بیایید این سازوکار را موشکافی کنیم.
دیتای واکنشپذیر (Reactive) چیست و چه تفاوتی با دیتای غیرواکنشپذیر دارد؟
چرا تمام متغیرهای تعریف شده در بدنه کامپوننت واکنشپذیر هستند؟
پشت صحنه آرایه وابستگی خالی [] چیست؟
چطور با ابزار Linter کدها را بدون نادیده گرفتن قوانین اصلاح کنیم؟
به کد زیر نگاه کنید؛ ما serverUrl و roomId را درون افکت خواندهایم، اما فقط roomId را در آرایه وابستگی آوردهایم:
const serverUrl = 'https://localhost:1234'; // 🟢 غیر واکنشپذیر (خارج از کامپوننت)
function ChatRoom({ roomId }) { // 🔴 واکنشپذیر (پروپ)
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]); // کاملاً درست
}
serverUrl نیازی به حضور در آرایه وابستگی ندارد؟چون serverUrl کاملاً خارج از بدنه کامپوننت تعریف شده است. این متغیر در جریان رندرهای پیاپی ریآکت ریرندر نمیشود، هیچوقت مقدارش تغییر نمیکند و به چرخه دادههای ریآکت متصل نیست. آرایه وابستگی فقط برای متغیرهایی است که در طول زمان تغییر میکنند.
اما اگر serverUrl را به یک استیت تبدیل کنیم، داستان عوض میشود:
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // 🔴 واکنشپذیر (استیت)
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]); // 💡 هر دو باید اینجا ذکر شوند!
}
حالا با تغییر آدرس سرور توسط کاربر، افکت متوجه تغییر این «مقدار واکنشپذیر» شده و اتصال قبلی را قطع و به سرور جدید متصل میشود.
یک تصور اشتباه این است که فقط پروپها (props) و استیتها (state) واکنشپذیر هستند. واقعیت این است که هر متغیری که در بدنه کامپوننت محاسبه یا تعریف شود، واکنشپذیر است. چون با هر بار ریرندر، آن متغیر از اول محاسبه میشود و مقدارش ممکن است تغییر کند.
به این مثال توجه کنید:
function ChatRoom({ roomId, selectedServerUrl }) { // پروپها واکنشپذیرند
const settings = useContext(SettingsContext); // کانتکست واکنشپذیر است
// این یک متغیر معمولی است، اما چون درون بدنه کامپوننت محاسبه شده، واکنشپذیر است!
const serverUrl = selectedServerUrl ?? settings.defaultServerUrl;
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]); // 💡 الزامی است که serverUrl در وابستگیها باشد
}
[] چیست؟اگر تمام متغیرها را به خارج از کامپوننت منتقل کنیم، افکت ما هیچ وابستگی واکنشپذیری نخواهد داشت:
const serverUrl = 'https://localhost:1234'; // غیر واکنشپذیر
const roomId = 'general'; // غیر واکنشپذیر
function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // ✅ آرایه خالی کاملاً قانونی است
}
از دیدگاه کامپوننت، این یعنی افکت فقط هنگام Mount اجرا و هنگام Unmount کلینآپ میشود. اما از دیدگاه افکت، آرایه خالی یعنی: «من کدهایی دارم که سیستم خارجی را همگامسازی میکنند، اما منطق من فعلاً به هیچ دیتای تغییرپذیری در رندر وابسته نیست.» اگر فردا روزی تصمیم بگیرید این متغیرها را به استیت تبدیل کنید، بدنه افکت دستنخورده باقی میماند و فقط نام آنها را به آرایه [] اضافه خواهید کرد.
اگر افزونه ESLint مربوط به ریآکت (eslint-plugin-react-hooks) در سورسکد شما فعال باشد، به محض جا انداختن یک متغیر واکنشپذیر به شما خطای معروفی میدهد:
}, []); // 🚩 Lint Error: React Hook useEffect has missing dependencies...
این خطا یک ایراد سیستمی یا سلیقهای نیست؛ لایم لایتر (Linter) به شما هشدار میدهد که کدهای درون افکت شما دارند از دیتای قدیمی (Stale) استفاده میکنند و در صورت تغییر رابط کاربری، افکت شما همگامسازی نخواهد شد.
بسیاری از توسعهدهندگان برای فرار از حلقههای بینهایت یا خطاهای کامپایل، قانون لینتر را با کامنت زیر خاموش میکنند:
// ❌ از این کار به شدت پرهیز کنید:
// eslint-disable-next-line react-hooks/exhaustive-deps
خاموش کردن لینتر مانند این است که چراغ هشدار بنزین ماشین را قطع کنید تا چشمتان را اذیت نکند! راهکار اصولی تغییر ساختار کد است:
انتقال به خارج از کامپوننت: اگر متغیر یا تابعی اصلاً نیازی به استیتها ندارد، آن را کاملاً به بیرون از کامپوننت منتقل کنید تا غیرواکنشپذیر شود.
انتقال به داخل افکت: اگر متغیر فقط و فقط درون همان افکت کاربرد دارد، تعریف آن را به داخل بدنه useEffect ببرید.
پرهیز از وابستگیهای شیء (Objects) و توابع: تعریف اشیاء و توابع در بدنه کامپوننت باعث میشود در هر رندر آدرس متفاوتی در حافظه بگیرند و افکت را به اشتباه بیپایان اجرا کنند (در درسهای آینده یاد میگیریم چطور با ابزارهایی مثل useMemo یا توابع خالص این مشکل را حل کنیم).
افکتها به تمام مقادیر استخراجشده از بدنه کامپوننت «واکنش» نشان میدهند.
هر دیتایی که درون کامپوننت تعریف شود (پروپ، استیت، کانتکست و متغیرهای محاسباتی)، واکنشپذیر (Reactive) است.
مقادیری که کاملاً خارج از کامپوننت هستند یا متغیرهای پایداری مثل توابع سِتاِستیت (setCount) پایدار بوده و نیازی به حضور در آرایه وابستگی ندارند.
شما حق انتخاب وابستگیها را ندارید؛ هر چیزی که درون افکت خوانده میشود، باید در آرایه قید شود. لینتر راهنمای وفادار شما در این مسیر است.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://react.dev/learn