ایجاد منوی سایدبار دینامیک به روش بازگشتی در React – از صفر تا صد
سایدبارها در صفحههای وب به دلیل کارکرد ناوبری که ارائه میکنند، یکی از مفیدترین اجزای موجود در صفحه محسوب میشوند. در این مقاله یک منوی سایدبار دینامیک مدرن به روش بازگشتی در ریاکت ایجاد میکنیم. روش بازگشتی (Recursion) تکنیکی است که به تابع امکان فراخوانی خودش را به صورت مکرر تا زمانی که شرط خاصی برقرار شود میدهد. هنگام استفاده از تکنیک بازگشتی در این مقاله از سه قاعده بازگشت، به شرح زیر بهره گرفته شده است:


- تابع باید شرطی داشته باشد که خودش را تخریب کند.
- تابع باید یک شرط مبنا داشته باشد.
- تابع باید خودش را فراخوانی کند.
سایدبارها در عمل جزئی ضروری از صفحه وب محسوب میشوند، هر چند در وهله اول توجه ما را جلب نکنند. دلیل این امر آن است که به کاربران کمک میکنند تا به روشهای مختلف در وبسایت حرکت کنند. به این ترتیب برای نمونه کاربران میتوانند به جای منوی ناوبری منطقی، از ناوبری محتوایی استفاده کنند.
اما شاید از خود بپرسید، چرا باید از تکنیک بازگشتی روی سایدبارها استفاده کنیم؟ این روش چه تفاوتی با نوشتن آیتمهای سایدبار به صورت دستی دارد؟ اگر کمی در اینترنت به گشتوگذار بپردازید، احتمالاً با سایدبار یک وبسایت مواجه میشوید که متوجه میشود آیتمهای سایدبار در واقع بخشهای فرعی وبسایت هستند. برخی سایتها سایدبارهایی دارند که آیتمهای خاصی را بر مبنای مسیر صفحهای که کاربر مراجعه کرده است، نمایش داده یا پنهان میکنند. این روش قدرتمندی است.
برای نمونه اگر به تصویر زیر نگاه کنید، درون دایره قرمز بخش ویرایشگران را به عنوان یکی از آیتمهای سایدبار و سه آیتم دیگر (Code Editor ،Markdown ،Text Editor) زیر آن را میبینید که بخشهای فرعی آن محسوب میشوند:

در انتهای این مقاله خواهیم دید که این سایدبار به ظاهر پیچیده با کمتر از 50 خط کد نوشته شده است. در ادامه مثالی از شیوه بسط کامپوننت سایدبار این مقاله به یک سایدبار شیکتر و در عین حال حفظ ظاهر تمیز آن را میبینید:

اینک بدون هر گونه توضیح اضافی شیوه انجام کار را میبینم. در این راهنما قصد داریم یک پروژه ریاکت را به سرعت با استفاده از اسکریپت create-react-app (+) ایجاد کنیم. در ادامه یک پروژه با استفاده از دستور زیر ایجاد میکنیم. در این راهنما نام پروژه خود را modern-sidebar (+) میگذاریم:
npx create-react-app modern-sidebar
اینک به دایرکتوری مربوطه میرویم:
cd modern-sidebar
در ادامه درون مدخل اصلی فایل src/index.js کمی تمیزکاری میکنیم تا بتوانیم روی خود کامپوننت متمرکز شویم:
اکنون فایلی به نام src/App.js ایجاد میکنیم:
کلاس App، کامپوننت Sidebar ما را با ساخت فایل Sidebar.js ایمپورت کرده و مورد استفاده قرار میدهد. بنابراین این فایل را ایجاد میکنیم:
اکنون قصد داریم یک کتابخانه CSS نصب کنیم، اما میتوانیم همین کارکرد سایدبار را بدون استفاده از آن نیز به دست آوریم. دلیل استفاده از این کتابخانه آن است که از جلوههای اضافی همراه با آیکونهای آماده آن استفاده کنیم.
npm install @material-ui/core @material-ui/icons
زمانی که کتابخانه فوق نصب شد، باید در مورد ساختار مبنای رابط کاربری که سایدبار خود را بر مبنhی آن خواهیم ساخت تأمل کنیم. یک راهحل این است که از عنصر لیست نامرتب (<ul>) استفاده کنیم که آیتمهای لیست (<li>) را رندر میکند. به این منظور List و ListItem را از material-ui/core@ ایمپورت میکنیم زیرا کامپوننت List اساساً یک عنصر ul است و کامپوننت ListItem نیز اساساً یک li است.
در ادامه اقدام به هاردکد کردن برخی آیتمها در سایدبار میکنیم تا شیوه نمایش ظاهری آن را دیده و اعتمادبهنفس خود را بالاتر ببریم. برخی اوقات کمی اعتمادبهنفس بیشتر میتواند به افزایش بهرهوری کمک کند:
فایل Sidebar.js
از disablePadding و dense برای کاهش اندکی در اندازه هر یک از آیتمها استفاده شود و یک prop به نام button برای افزودن جلوه ripple مورد استفاده قرار گرفته است. تاکنون به این نتیجه دست یافتهایم:

اکنون که اعتمادبهنفس خود را بالا بردهایم، نوبت آن رسیده است که props.items را تعریف کنیم که Sidebar برای رندر آیتمهایش به آن وابسته است. معنی حرف فوق این است که باید یک prop به نام items داشته باشیم که شامل آرایهای از اشیا برای نمایش هر آیتم در منوی سایدبار باشد. ما میخواهیم کاربرد را تا حد امکان، ساده حفظ کنیم چون در غیر این صورت کامپوننت به سرعت پیچیدهتر از حد قبل کنترل میشود. ابتدا آیتمها را در کامپوننت App ایجاد کرده و آن را به صورت props.items به Sidebar ارسال میکنیم:
فایل App.js
اکنون کامپوننت Sidebar را بهروز میکنیم تا ساختار این آرایه را نشان دهیم.
فایل Sidebar.js
نکتهای که شاید متوجه شده باشید، این است که سایدبار ما بیش از حد بزرگ است. سایدبارها به طور معمول یک بخش از صفحه را اشغال میکنند. از این رو قصد داریم اندازه آن را تا حد مطلوب کاهش دهیم. در ادامه مقدار max-width را روی 200px تنظیم میکنیم. از این رو باید یک عنصر div ایجاد کنیم که کامپوننت List را درون آن قرار دهیم.
دلیل این که به جای اعمال مستقیم استایلها روی کامپوننت List، عنصر div دیگری ایجاد میکنیم این است که نمیخواهیم List مسئول اندازه عرض باشد. بدین ترتیب در آینده میتوانیم List را در یک کامپوننت سایدبار با قابلیت استفاده مجدد انتزاع کنیم که با هر اندازهای بسته به عنصر والد قابلیت تطبیق دارد. کامپوننت Sidebar.js اینک به صورت زیر در آمده است:
در ادامه درون فایل index.css استایلهای CSS را برای کلاس Sidebar تعریف میکنیم:
Material-UI مستقیماً از سازوکار استایلبندی CSS (+) خود با استفاده از رویکرد CSS-in-JS بهره میگیرد، اما در این مقاله از CSS معمولی استفاده میکنیم تا موارد مختلف به صورت غیر لازمی پیچیده نشوند.
اینک میتوانیم آن را به همین صورت ابتدایی رها کنیم و در آینده آن را فراخوانی نماییم. با این حال از آیتمهای فرعی پشتیبانی نمیکند. ما میخواهیم بتوانیم روی یک آیتم سایدبار کلیک کنیم و در صورتی که آیتم فرعی داشته باشد، آنها باز شوند. داشتن آیتم فرعی به سازماندهی سایدبار با گروهبندی آیتمهای اضافی درون یک سایدبار دیگر کمک میکند:

روشی که برای پشتیبانی از این قابلیت استفاده خواهیم کرد این است که یک گزینه درون هر آیتم سایدبار قرار میدهیم تا کامپوننت بتواند آیتمهای فرعی آن را تشخیص دهد. آرایه آیتمها در کامپوننت App را طوری تغییر میدهیم که آیتمهای فرعی را ارسال کنیم:
برای این که بتوانید آیتمهای فعلی یک آیتم سایدبار را رندر کنید، باید مشخصه items را در زمان رندر کردن آیتمهای سایدبار زیر نظر بگیرید:
چنان که در تصویر زیر میبینید، کامپوننت سایدبار به هم ریخته است:

این آن سایدباری نبود که ما میخواستیم به آن دست پیدا کنیم. اکنون از آنجا که نمیخواهیم کاربران وبسایتمان هنگام مراجعه به وبسایت ما دکمه بستن را زده و دیگر هرگز به آن بازنگردند، باید روشی برای بهتر ساختن ظاهر وبسایت هم برای چشمها و همچنین DOM وبسایت پیدا کنیم.
شاید بپرسید منظور از DOM چیست؟ اگر دقیقتر نگاه کنید مشکلی وجود دارد. اگر کاربر روی یک آیتم فرعی کلیک کند، آیتم والد، آیتم فرعی را رندر میکند و «دستگیره کلیک» (Click Handler) کاربر را نیز مصرف میکند، چون آنها با هم همپوشانی دارند. این وضعیت بدی است و مشکلات غیرمنتظرهای برای تجربه کاربر رقم میزند. اینک به جداسازی والد از فرزندانش (آیتمهای فرعی) نیاز داریم، به طوری که آیتمهای فرعی خود را به صورت همزمان رندر کنند و بدین ترتب رویدادهای ماوس با هم تصادم نداشته باشند:
اکنون تقریباً همه چیز به حالت عادی بازگشته است:

با این حال در تصویر فوق به نظر میرسد که با مشکل جدیدی مواجه شدهایم. مشکل جدید این است که آیتمهای فرعی به طرز شدیدی بزرگتر از آیتمهای سطح بالا هستند. ما باید روشی برای تشخیص این که کدام موارد آیتم فرعی و کدام یک آیتمهای سطح بالا هستند، داشته باشیم. میتوانیم آن را به روش هاردکد بنویسیم.
فایل Sidebar.js
فایل styles.css
اما میدانیم که کامپوننت سایدبار ما باید دینامیک باشد. به طور معمول میخواهیم آیتمهای سایدبار بر اساس آیتمهای ارسالی در props از سوی فراخواننده ایجاد شوند. به این منظور قصد داریم از یک prop ساده به نام depth استفاده کنیم که آیتمهای سایدبار مورد بهرهبرداری قرار میدهند و بر مبنای آن، عمقی که دارند را تنظیم میکنند و دیگر مهم نیست که چه قدر در درخت سایدبار فرو میرویم. همچنین باید آیتم سایدبار را در کامپوننت خودش استخراج کنیم به طوری که بتوانیم عمق را بدون درگیر شدن با منطق حالت آغازین افزایش دهیم. کد به صورت زیر است.
فایل Sidebar.js
در کد فوق برخی prop-های قدرتمند اعلان کردهایم تا فاز پیش رندر سایدبار مانند depth و depthStep را پیکربندی کنیم. SidebarItem به صورت کامپوننت خودش استخراج میشود و درون بلوک رندرش از depth برای محاسبه فاصلهبندی استفاده میکند. هر چه depth بالاتر باشد، در موقعیت عمیقتری در درخت قرار گرفته است. همه این موارد به دلیل وجود کد زیر ممکن شدهاند:
depth هر بار که لیست جدید به آیتمهای فرعی اضافه شده و عمق بیشتری مییابد 1 واحد افزایش پیدا میکند. درون SidebarItem فرایند بازگشتی وجود دارد، زیرا خودش را تا زمانی که دیگر کد مبنا وجود نداشته باشد فراخوانی میکند. به بیان دیگر زمانی که آرایه خالی شد، این قطعه کد به صورت خودکار متوقف میشود:
اینک کامپوننت سایدبار بازگشتی را تست میکنیم.
فایل src/App.js

چنان که میبینید بالاخره موفق شدیم. اینک اندکی دیگر روی depthStep کار کرده و مقدار بالاتری را ارسال میکنیم:

سخن پایانی
سورس کد این راهنما را میتوانید از این ریپوی گیتهاب (+) دانلود کرده و قابلیتهای اضافی سایدبار را ببینید. این سایدبار کارکردهای جالبتری مانند افزودن یک لایه اضافی در رندرینگ دارد که منجر به نمایش جداکنندهها، باز کردن/ بستن سایدبار، آیکونها و غیره شده است.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- آموزش ساخت پروژه با فریم ورک React Native
- مجموعه آموزشهای برنامهنویسی
- 5 ابزار برای تسریع فرایند توسعه در React — راهنمای کاربردی
- آموزش ری اکت (React) — مجموعه مقالات مجله فرادرس
==












