Prop Drilling در React چیست؟

Simon Caspersen تصویری از در این مقاله به جای prop drilling از حفاری مشخصه استفاده شده و در برخی قسمت های مقاله به جای prop از معادل مشخصه استفاده شده است.حفاری مشخصه چیست؟ چرا خوب و چرا بد است و چگونه از مشکلاتی معمولی که در آن رخ میدهند اجتناب کرد؟این پست قصد دارد به شما در فهم prop drilling یا حفاری مشخصه(که همچنین threading یا نخ کشی هم نامیده میشود) کمک کند و توضیح دهد چه زمان هایی میتواند مشکل ساز باشد و راهکارهایی جهت دور ماندن یا اجتناب از این مشکلات را برای شما ارائه دهد.حفاری مشخصه ها یا prop drilling یکی از روش هایی است که در نوشتن کامپوننت ها در React مورد استفاده قرار میگیرد.حفاری مشخصه یا prop drilling چیست؟حفاری مشخصه به پروسه ای اشاره دارد که برای گرفتن اطلاعات در درخت کامپوننت های React باید انجام گیرد. برای درک بهتر مطلب به کامپوننت زیر توجه کنید:حالا این کامپوننت را به دو کامپوننت تبدیل میکنیم:ساده بود! کامپوننت Switch نیاز دارد تا به toggle و on ( که state کامپوننت Toggleاست) اشاره کند برای همین آنها را در قالب props برای او ارسال میکنیم. دوباره کد بالا را بازنویسی میکنیم و لایه دیگری به درخت کامپوننت‌ها اضافه میکنیم:این فرایند حفاری مشخصه ها نامیده میشود. برای اینکه on و toggle را به مکان موردنظر برسانیم باید حفاری (که نخ کاری هم نامیده میشود) تا کامپوننت Switch انجام دهیم. کامپوننت Switch برای عملکرد خودش دراصل نیازی به مقادیری که برایش ارسال میشوند ندارد اما باید مشخصه ها را برای این کامپوننت ارسال و دریافت کنیم زیرا فرزند(ان) این کامپوننت به آنها نیاز دارند.به فرایند ارسال props از والد به فرزندان به صورت آبشاری در درخت کامپوننت ها را prop drilling (حفاری مشخصه) یا threading(نخ کشی) میگویند.چرا Prop Drilling خوب است؟آیا هیچوقت روی برنامه ای که از متغیرهای سراسری استفاده کرده است کار کرده اید؟ اپلیکیشنی که با AngularJs نوشته شده و از وراثت غیر مجزا scope$ استفاده کرده است چطور؟ علت اینکه جامعه برنامه نویسان بصورت گسترده این متدولوژی(استفاده از متغیر سراسری) را کنار گذاشته این است که این کار ناچاراً موجب مدل داده‌ی بسیار گیج کننده ای در اپلیکیشن شما میشود. با این کار پیدا کردن جایی که داده مقدار اولیه گرفته، آپدیت شده و مورد استفاده قرار گرفته است سخت میشود.پاسخ دادن به این سوال که "آیا میتوانم این کد را ویرایش یا حذف کنم بدون اینکه منجر به خرابکاری شود؟" در این مدل نوشتن سخت است. این همان سوالی است هنگام کدنویسی باید برای آن بهینه سازی انجام دهید. به همین دلیل است که استفاده از ماژول ها به متغیرهای سراسری ترجیح داده میشود.از جمله دلایلی که ماژول ها را به متغیرهای سراسری ترجیح میدهیم این است که: ماژول ها ما را قادر میسازند درمورد محلی که مقادیر(اعم از توابع و آرایه ها و ورودی ها و...) مورد استفاده قرار میگیرند صریح تر عمل کنیم.دنبال کردن و یافتن مقادیر را برای ما بسیار ساده تر میکند.پروسه تشخیص دادن اینکه تغییر در قسمتی از برنامه چه تاثیری روی قسمت های دیگر میگذارد را ساده میکند.حفاری مشخصه ها در ابتدایی ترین سطح خود تنها مقادیر را در سراسر بخشی از برنامه عبور میدهد. این عالی است! زیرا در مثال بالا اگر تصمیم گرفتید در Toggle و onتغییری ایجاد کنید و مثلا به جای اینکه on از نوع state باشد از نوع enum باشد، به راحتی میتوانید همه جاهایی on که مورد استفاده قرار گرفته است را با دنبال کردن کد بصورت ایستا (بدون نیاز به اجرای برنامه) انجام دهید و آپدیت موردنظر خود را پس از بررسی کردن اعمال کنید. موضوع مهم در این رویکرد صریح بودن در مقابل غیرصریح بودن است.در این روش دنبال کردن مراحل ارسال و دریافت مشخصه ها بصورت واضح معلوم است و میتوان تنها با دنبال کردن روند دریافت مشخصه ها توسط کامپوننت ها، تغییرات احتمالی در برنامه را پیش بینی کرد.حفاری مشخصه منجر به چه مشکلاتی میشود؟در مثال فرضی بالا هیچگونه مشکلی ایجاد نخواهد شد، اما به محض گسترده تر شدن اپلیکیشن، خود را در حال گشتن میان لایه های زیادی از کامپوننت ها خواهید دید. هنگامیکه برای اولین بار به اینصورت مینویسید بطور معمول مساله مهمی نیست ولی هنگامیکه چند هفته از کار کردن کدها گذشت در چند مورد اتفاقاتی در برنامه شما خواهد افتاد که دنبال کردن آنها سخت خواهد بود:تغییر شکل قالب برخی داده ها برای شما مشکل خواهد شد مثلا {user: {name: 'Joe West'}} -> {user: {firstName: 'Joe', lastName: 'West'}})ارسال بیش از حد propها (بیشتر از آن چیزی که ضرورت دارد) به علت حذف یا تغییر مکان یک کامپوننت که نیاز به برخی propها داشته ولی حالا دیگر نیازی به آنها نیست.ارسال مجدد propهااستفاده نامناسب از مقادیر پیش فرض برای propها. به همین دلیل متوجه مفقود شدن propها نمیشوید.تغییر نام propها در میانه مسیر ارسال ها دنبال کردن prop را برای شما مشکل خواهد ساخت.علاوه بر شرایط بالا، شرایط مختلف دیگری نیز وجود دارد که در آن حفاری مشخصه می تواند مشکلات واقعی را در روند بازنویسی و اصلاح کد ایجاد کند.چگونه میتوانیم از مشکلاتی که بوجود می آورد اجتناب کنیم؟کامپوننت ها را تا زمانی که نیاز نیست قطعه قطعه نکنید.یکی از مسائلی که به راستی ایجاد مشکل میکند، شکستن متدrender در چندین کامپوننت است، این شکستن درحالی صورت میگیرد که لزومی به اعمال چنین فرایندی نیست. شگفت زده میشوید اگر بدانید یک render بزرگ چقد ساده خواهد شد هنگامیکه تا حد ممکن بصورت درون خطی (inline) آن را بنویسید(نه در چندین کامپوننت مجزا). هیچ دلیلی ندارد که زودتر از زمانی که نیاز است شروع به قطعه قطعه کردن کامپوننت ها کنید. کامپوننت ها را قطعه قطعه نکنید تا زمانی که واقعا نیاز دارید یک بلاک از کد را مجددا استفاده کنید. هنگامیکه اینگونه عمل کنید نیاز به ارسال propها نخواهید داشت.از مقدار پیش فرض برای propهایی که عملکرد کامپوننت به آنها وابسته است استفاده نکنید.کار دیگری که میتوانید برای به حداقل رساندن تاثیر حفاری مشخصه انجام دهید این است که از مقادیر پیش فرض برای propهایی که داشتن مقدار برای آنها ضروری است اجتناب کنید. استفاده از مقدار پیش فرض برای propهایی که مقدار داشتن برای آنها ضروری است تا کامپوننت به درستی عمل کند، تنها باعث میشود خطاهای مهم پنهان شوند و برخی اعمال به درستی انجام نگرفته و با شکست مواجه شوند.پس تنها زمانی که مقادیر پیش فرض استفاده کنید که مقدار propها ضروری نیست.تا جایی که ممکن است stateها را نزدیک نگه دارید.تا جایی که ممکن است stateها را از کامپوننت ها دور نکنید.تنها اگر زمانی یک قسمت از برنامه شما نیاز داشت این کار را انجام دهید به شرطی که به والد خود تا حد ممکن نزدیک باشد و در این حالت state را در بالاترین سطح برنامه قرار ندهید تا به فرزندان مقدار state را ارسال کنید.در صورتیکه عمیق شدن در درخت کامپوننت های react ضروری بود از Context Api استفاده کنید. این اتفاق همیشه رخ نخواهد داد که در هر جایی به آن نیاز داشته باشیم.این کار واقعا به شما کمک خواهد کرد تا از مشکلاتی که حفاری مشخصه ایجاد میکند دور بمانید. اشاره شد که Context به نوعی ما را به روزهای متغیرهای سراسری بازمی گرداند. با این تفاوت بخاطر روشی که این API طراحی شده است شما کماکان بصورت ایستا میتوانید context provider و consumer را نسبتا به سادگی به دست بیاورید و روند تغییرات را در آنها دنبال کنید. منبع: Prop Drilling ‌By Kent.c Dodds