چرا نمیتوانم مستقیماً وضعیت یک کامپوننت را تغییر دهم؟
درک این نکته که چرا مستقیماً نباید وضعیت (state) یک کامپوننت در ریاکت تغییر داده شود و باید از setState استفاده کرد، برای برنامهنویسی مؤثر در ریاکت ضروری است. اگرچه در نگاه اول ممکن است به نظر برسد که تغییر مستقیم وضعیت و سپس فراخوانی this.setState({}) برای تحریک رندر کار میکند، اما این روش خطرات پنهانی دارد که در مقیاسهای بزرگتر آشکار میشوند.
ریاکت از الگوی جریان داده یکطرفه (unidirectional data flow) پیروی میکند که به آن معماری تابعی میدهد. این الگو بر مبنای اصل تغییرناپذیری (immutability) استوار است. در این الگو، هرگونه تغییر در وضعیت، ابتدا به صورت یک کپی از وضعیت فعلی ایجاد میشود و تغییرات روی این کپی اعمال میشوند. سپس، این کپی جدید با استفاده از setState به عنوان وضعیت جدید کامپوننت تنظیم میشود.
تغییر مستقیم وضعیت، این جریان یکطرفه را مختل میکند. زمانی که شما وضعیت را مستقیماً تغییر میدهید و سپس setState() را با یک شیء خالی فراخوانی میکنید، ممکن است ریاکت نتواند به درستی تغییرات را شناسایی کند و رندر بهروزرسانی نشود یا بهطور نادرست بهروزرسانی شود. این به دلیل مکانیسم مقایسه سطحی (shallow compare) ریاکت است که تفاوتها بین وضعیت قدیمی و جدید را تشخیص میدهد. اگر وضعیت به طور مستقیم تغییر یافته باشد، ریاکت ممکن است این تغییرات را تشخیص ندهد و بنابراین رندر بهروزرسانی نشود.
علاوه بر این، تغییر مستقیم اشیاء و آرایهها در جاوااسکریپت به این معنی است که شما فقط یک مرجع به شیء یا آرایه اصلی ایجاد میکنید. هرگونه تغییر در این مرجع، تمام مراجع دیگر به آن شیء یا آرایه را نیز تحت تأثیر قرار میدهد. ریاکت برای مدیریت این مورد، مکانیسمهای داخلی دارد، اما استفاده از setState تضمین میکند که این مکانیسمها به درستی عمل میکنند.
تغییر مستقیم وضعیت میتواند به مشکلات زیر منجر شود:
- همپوشانی تغییرات: ریاکت ممکن است تغییرات مستقیم اعمالشده را با تغییرات در صف
setStateهمپوشانی کند و برخی تغییرات از بین بروند یا نادیده گرفته شوند. - مشکلات در مقیاسپذیری: در پروژههای بزرگ، تغییر مستقیم وضعیت میتواند به کد نامنظم و غیرقابلمدیریت منجر شود، به خصوص اگر چندین جزء به یک وضعیت خاص دسترسی داشته باشند.
- مشکل در دیباگینگ: خطا یابی در کدی که وضعیت به صورت مستقیم تغییر یافته باشد میتواند دشوار باشد.
- رندرهای غیرضروری: اگر روش
shouldComponentUpdateبه درستی پیادهسازی نشده باشد، تغییر مستقیم وضعیت میتواند به رندرهای غیرضروری و کاهش کارایی منجر شود.
برای ایجاد کپی از اشیاء تو در تو و آرایهها، باید از روشهای کپی عمیق (deep copy) استفاده کنید. روشهای ساده مانند slice یا de-structuring ES6 تنها کپی سطحی ایجاد میکنند و برای اشیاء تو در تو مناسب نیستند. کتابخانههای مانند Lodash میتوانند برای این منظور مفید باشند (_.cloneDeep). همچنین روش JSON.parse(JSON.stringify(obj)) میتواند مورد استفاده قرار گیرد، اما توجه داشته باشید که این روش با اشیاء دارای مراجع دایرهای کار نمیکند.
به طور خلاصه، اگرچه ممکن است در مثالهای کوچک تغییر مستقیم وضعیت بدون مشکل به نظر برسد، اما این روش یک رویکرد نادرست است و میتواند در پروژههای بزرگتر به مشکلات جدی منجر شود. استفاده از setState برای تغییر وضعیت، شیوهای صحیح و توصیهشده در ریاکت است که تضمین میکند الگوی جریان داده یکطرفه و مکانیسمهای داخلی ریاکت به درستی عمل میکنند. این کار به نگارش کد قابلمدیریتتر، قابل نگهداریتر و پایدارتر کمک میکند.