در طی سال هایی که به صورت حرفه ای مشغول برنامه نویسی هستم تقریبا در هیچ زمانی از نحوه کد زدنم راضی نبودم تا اینکه یک بار گفتم بهتره یه تغییراتی توش بدم و ببینم مشکل کجاست و کجای کارم رو میتونم بهبود بدم.
این ماجرا مربوط به تجربیات من در Refactor کردن بخشی از یکی از محصولات شرکتمون هست که دوست دارم اون تجربیات رو با شما به اشتراک بذارم.
احتمالا در مورد راه و روشهایی که قصد دارم در موردشون صحبت کنم قبلا چیزهایی شنیدید اما این مقاله صرفا در مورد اینه که من چطور مسائل رو حل کردم و این پازل رو کنار هم چیدم.
خوب ... شروع کنیم!
راه های مختلفی برای ساختار دادن به پروژه های React وجود داره اما خوشبختانه و یا متاسفانه React و در کل JavaScript برای پروژه شما ، ساختاری رو مشخص نمیکنه و دست شما برای چیدمان فایلها و امکانات سیستم کاملا بازه که البته از یک منظر خوب و از منظر دیگر خیلی هم خوب نیست !
بر مبنای مواردی که در داکیومنت React پیشنهاد شده میتونیم فایلهای پروژه رو به دو صورت تقسیم کنیم :
بر اساس تجربه من که در هر دو مدل کار کردم ، مورد دوم تجربه بهتر و راحت تری بود.
در اینجا مثالی از نحوه ای که من این کار رو انجام دادم می بینید:
به نظر که خیلی مرتب و تمیز میاد ! اینطور نیست ؟ پس بهتره با هم یکم موضوع رو عمیق تر بررسی کنیم.
در این مدل فرض کنید یک تیم در حال توسعه یک اپلیکیشن هست و هر کدوم از برنامه نویس ها روی یک Feature کار می کنند ، در این حالت ممکنه تعداد خیلی زیادی فایل ایجاد بشه که شما در طی کارتون بهشون نیازی نداشته باشید و یا حتا براتون دردسر ساز هم بشن ! بعنوان مثال ، یک برنامه نویس مبتدی کد نیمه کاره ای رو Push کرده که در روند توسعه کد شما اخلال بوجود آورده و یا خیلی از موارد پیش بینی نشده دیگه ...(امیدوارم متوجه اصل داستان شده باشید)
در یک چنین وضعیتی همینطور که روند پروژه پیش میره و پروژه بزرگ و بزرگتر میشه ، بواسطه بوجود اومدن این همه فایل و فولدر ضریب احتمال خطا بسیار بالا میره و چه بسا در نهایت ساختار یکدست پروژه و کل سیستم مختل میشه.
در این شرایط امکان بوجود اومدن مشکل Require Cycle هم افزایش پیدا میکنه.
و اما برگردیم سر اصل موضوع که همون مدل قابلیت-محور (Feature-Base)بود:
طبق این مدل هر کدوم از قابلیت های اپلیکیشن به همراه همه نیازمندی ها و وابستگی هایی که داره داخل یک فولدر Pack میشه، بعنوان مثال برای توسعه صفحه کمپین ها ما فقط با یک فولدر سر و کار داریم، درنتیجه بعد از Scale شدن کار کاملا مشخصه که هر فایل و کامپوننت مربوط به کجاست و مربوط به چه Feature ای هست!
همینطور در مورد تست نوشتن ها که میتونه برای هر Feature داخل فولدر خودش انجام بشه که طبعا با بقیه امکانات سیستم تداخلی نداره و همچنین امکان بوجود اومدن مشکل Require Cycle هم کمتر میشه و میتونیم بخشهای دیگه رو هم راحتتر به اون اضافه کنیم.
ما از Functional component ها استفاده میکنیم چون:
در مورد هر کدوم از موارد بالا میشه یک مقاله مفصل نوشت! اما به طور مختصر میتونیم بگیم معمولا برای رندر کردن اجزای کوچک صفحه نیازی به استفاده از Class component ها نیست و میشه خیلی ساده با یک تابع ساده اون ها رو بوجود آورد. خب چرا که نه؟!
در صورتی که یک منطق مشترک یا رفتار مشابه بین چند کامپوننت دارید یا میخواید دیتای خاصی به همه کامپوننت های زیر مجموعه تزریق بشه میتونید از HOC ها استفاده کنید.
مثلا میتونید وضعیت Network رو در یک HOC بررسی کنید و در همه کامپوننت های زیر مجموعه وضعیت اون رو نمایش بدید یا کنترل کنید.
یا اینکه تصور کنید قصد دارید حرکت کاربر بین صفحات اپلیکیشن رو تحت نظر داشته باشید و لاگ اون رو ثبت کنید . طبیعتا باید یک تابع رو بار ها و بار ها در مواقع مختلف صدا بزنید که در این حالت میتونید با استفاده از یک HOC این منطق رو پیاده سازی کنید و اون رو بین همه صفحات دیگه به اشتراک بزارید و همینطور اطلاعات مربوط به اشتراکات این تبلیغات رو بین صفحات پخش کنید. (در واقع شما با اینکار DRY کد میزنید.)
هر کامپوننت بزرگ میتونه به کامپوننتهای کوچکتر تقسیم بشه که این موضوع به زیباتر شدن و قابل فهم تر شدن کاپوننتها برای ما کمک زیادی میکنه ، بعنوان مثال به کامپوننت زیر توجه کنید:
که میتونه به کاپوننتهای کوچکتری تقسیم بشه:
و در نهایت در فایل components.js داریم:
خیلی ساده اما بسیار کارامد!
لینتر ابزار خیلی خوبیه برای اینکه بتونید کد تمیز تر، کم ایراد تر و به طور کل با کیفیت تری بنویسید.
کدهایی که پتانسیل تبدیل شدن به ارور رو دارند رو بهتون نشون میده، مجبورتون میکنه طبق قواعدی خاص کد بزنید (که این قواعد رو یا از Code style های معروف مثل Airbnb بردارید یا خودتون برای خودتون اون رو بوجود میارید) که این باعث یکدست شدن دست خط شما با اعضای دیگه تیم میشه.
این مثال ساده رو در نظر بگیرید:
میتونید از پلاگین هایی که برای VSCode ساخته شدن هم استفاده کنید که دلیل هر یک از این تغییرات رو هم براتون به شکل یک صفحه documentation نشون میده. فقط کافیه در VSCode داخل تب Extenstions کلمه ESLint رو سرچ کنید و پر دانلود ترین رو انتخاب و نصب کنید.
به کمک قابلیت جدید React یعنی hook ها! شما امکان داشتن state حتی داخل functional-component ها رو خواهید داشت. قبلا برای نمایش اطلاعات داخل functional-component ها شما میبایست حتما به وسیله props این کار رو انجام میدادید ولی حالا hook ها این امکان رو به ما میدن که داخل functional-component ها هم state و اطلاعات خاص خودشون رو داشته باشیم.
اما اینجا میخوام در مورد custom hooks صحبت کنم
همونطور که در شکل میبینید این یک کامپوننت معمولیه که لیست گل ها رو دریافت میکنه و در لیست نشون میده.
برای دریافت گل ها از hook ها استفاده کردیم. اما خب بهتره که منطق دریافت اطلاعات رو به جای دیگه ای ببریم و اینجا رو خلوت تر کنیم!
برای همین یک custom hook مینویسیم که برای ما اینکارو انجام بده:
خب حالا منطق دریافت اطلاعات گل ها رو جدا کردیم و الان میتونیم از این منطق توی کامپوننت اصلیمون استفاده کنیم.
همونطور که میبینید در منطق و کارکرد این کامپوننت تغییری ایجاد نمیشه ولی درنهایت کد بسیار تمیزتر و خوانا تر هست که تست کردن هردوی این توابع بهینه تر و حتی نوشتن تست هم ساده تر میشه.
این موضوع دیگه تکراری و کلیشه ای شده که کد شما باید همیشه بصورت self-document باشه به این معنی که با رعایت کردن چند convention برای نام گذاری ویا رعایت بعضی best practice ها و استفاده از ساختار های استاندارد، کاری کنیم تا کسی برای فهمیدن و سر در آوردن کدهای ما خودش رو به زحمت نیندازه و به راحتی براش قابل فهم باشه.
گرچه این مساله کاملا به جا و درسته اما چیزی از ارزشهای والای کامنت گذاری کم نمیکنه.
خلاصه اینکه بهتره سعی کنیم برای قسمت هایی از کد که ممکنه فهمشون مشکل تر از بقیه سیستم باشه کامنت هایی رو قرار بدیم که نحوه کار اون قسمت رو خوب توضیح بده. مثلا این میتونه در مورد توابع ورودی ها و خروجی تابع باشه.
همچنین میتونیم از استاندارد هایی مثل Jsdoc هم استفاده کنیم که این امور رو برای ما ساختار یافته تر انجام بده.
میتونید لیستی از همه نکته ها و مواردی که میتونید توی کامنت ها بنویسید رو توی این لینک ببینید.
همه مون این مورد رو دیدیم:
مشکل ما با این روش (طولانی و بعضا گیج کننده بودن آدرسها) در صورت جابجایی فایلها و موارد دیگه ، خراب شدن و ازبین رفتن پروژه است که این موضوع زمانی بیشتر خودنمایی میکنه که این کامپوننت ها و ماژولها خیلی پر استفاده هم باشند ، اما راه حل بسیار ساده است.
میتونیم خیلی ساده هر کدوم از این اجزا رو به یک ماژول npm تبدیل کنیم و هرجایی که نیاز داشتیم شکل یک پکیج third-party اونها رو import کنیم.
همینطور که میبینیم کار رو برای ما ساده تر میکنن.
داخل هر فولدری که خواستید این پکیج ها رو قرار بدید و توی اون فولدر یک فایل package.json ایجاد کنید با این محتوا:
بعد از project root دستور npm install PACKAGE_ADDRESS (به جای PACKAGE_NAME آدرس اون فولدر کاستوم پکیج رو وارد کنید) اجرا کنید تا اضافه بشه.
امیدوارم این نکات براتون مفید بوده باشه. خوشحال میشم نظرات تون رو برام بنویسید.
موفق باشید ?