درود دوستان 👋 توی این پست میخوایم یکی از رایجترین الگوهای انتقال اطلاعات توی یک برنامهٔ ریاکتی به اسم Prop Drilling رو بررسی کنیم و با مزایا و معایب اون آشنا بشیم. این پست از مجموعه پستهای ریاکت ۱۰۱ هست که شامل مجموعهای از پستهای کوتاه و کاربردی ریاکتی میشه.
Prop Drilling چیه؟ 🤔
Prop drilling به یک الگو یا روش پاس دادن اطلاعات توی ریاکت گفته میشه که توی اون، اطلاعات باید از چندین لایهٔ کامپوننتِ تو در تو عبور کنه تا به مقصدش برسه. این اتفاق زمانی رخ میده که توی یک کامپوننت والد میخوایم به کامپوننت داخلی توی لایههای زیرین اطلاعات پاس بدیم. اطلاعات به صورت پراپرتی (Prop) از لایههایی از کامپوننتها عبور میکنه تا به کامپوننت مقصد برسه:
function Parent() { const user = { name: "John Doe", age: 30 }; return <Child1 user={user} />; } function Child1({ user }) { return <Child2 user={user} />; } function Child2({ user }) { return <Child3 user={user} />; } function Child3({ user }) { return <h2>Username: {user.name}</h2>; } /* Parent │ └── Child1 (→ user) │ └── Child2 (→ user) │ └── Child3 (→ user, renders <h2>) */
توی مثال بالا همونطور که میبینیم کامپوننت Child3 از پراپرتی user استفاده میکنه و اطلاعات کاربر رو نمایش میده. اما ساختار این کامپوننتها طوری هست که برای پاس دادن آبجکت user به Child3 باید اون رو از مسیر کامپوننت Child1 و Child2 عبور بدیم تا به مقصد نهاییش یعنی Child3 برسه. این ساختار مشکلاتی رو ایجاد میکنه که توی ادامه اونها رو بررسی میکنیم:
۱. افزایش پیچیدگی
وقتی برنامه بزرگ و بزرگتر میشه، مدیریت این ارتباطات پیچیدهتر میشه و خوانایی و توسعهٔ برنامه رو سخت میکنه.
۲. رندرهای غیر ضروری
حتی که کامپوننتهای میانی نیازی به اون پراپرتی نداشته باشن، وقتی کامپوننت والد رندر میشه، کامپوننتهای میانی هم مجدد رندر میشن.
۳. کامپوننتهای غیر قابل استفاده مجدد
وقتی یک کامپوننت به اطلاعاتی وابسته باشه که به اون احتیاجی نداره، هر جای دیگه از برنامه که میخوایم از اون کامپوننت استفاده کنیم میبایست اون پراپرتی رو برای کامپوننت تأمین کنیم. پس به قول معروف Reusability اون کامپوننت کاهش پیدا میکنه.
چطوری از این مشکل جلوگیری کنیم؟
برای حل این مشکل راه حلهای متنوعی وجود داره که توی ادامه به ۳ تا از معروفترین اونها اشاره میکنیم.
۱. استفاده از Context API
یکی از معروفترین راههای برای حذف کردن کامپوننتهای واسط اینه که از Context API استفاده کنیم. توی این روش، یک کامپوننت به اطلاعات مد نظرش به صورت مستقیم دسترسی داره.
۲. استفاده از State Management ها
یک روش مرسوم دیگه استفاده از State Management هایی مثل Redux و MobX هست تقریباً کارایی مشابه Context API دارن اما معمولاً برای مصارف پیچیدهتر به کار میرن. اگه ساختار ما خیلی ساده هست، بهتره که از Context API استفاده کنیم (توی یک پست جدا بررسی میکنیم که چه زمانی از Context API و چه زمانی از State Management استفاده کنیم.)
۳. استفاده از Custom Hook
استفاده از یک هوک کمک میکنه از تکرار کدها جلوگیری کنیم و همچنین مثل قابلیتی که روشهای بالا به ما میدن، میتونیم کاری کنیم که اطلاعات مد نظر ما به صورت مستقیم توی هر کامپوننت و توی هر سطحی قابل دسترس باشن. همچنین ترکیب Context API و یک Custom Hook روش خیلی خوبی به حساب میاد. ابتدا کانتکست رو میسازیم:
import { createContext, useContext } from "react"; // 1. Create Context const UserContext = createContext(); // 2. Create Provider Component function UserProvider({ children }) { const user = { name: "John Doe", age: 30 }; return <UserContext.Provider value={user}>{children}</UserContext.Provider>; }
و بعد هوک که توی خودش این کانتکست رو پیادهسازی میکنه:
// 3. Custom Hook for Accessing User function useUser() { return useContext(UserContext); }
و بعد پیادهسازی Provider:
// 4. Implement Provider function main() { return ( <UserProvider> <App /> </UserProvider> ); }
و نهایتاً استفاده از هوک:
function Child3() { const user = useUser(); // Get user data directly return <h2>Username: {user.name}</h2>; }
همونطور که دیدیم بدون نیاز به تعریف پراپرتی و یا پاس دادن پراپها از کامپوننتهای بالایی تونستیم کاری کنیم که متغیر user توی کامپوننت Child3 قابل دسترس باشه 👌
خب دوستان به پایان این قسمت رسیدیم. Prop drilling روشی هست که برای استفادههای ساده مناسب هست. اما وقتی برنامه پیچیدهتر میشه استفاده از این روش محدودکننده هست و باید سراغ روشهای دیگه بریم که توی این پست با اونها آشنا شدیم.
تا یه پست دیگه روزتون خوش 😉🌹
