به نام خدا
سلام
در این پست میخوایم به بررسی تغییرات و ویژگی های نسخه جدید React Router یعنی نسخه 6 بپردازیم.
یکی از نکات مثبت این نسخه، مستندات بهتر و ظاهر تر و تمیزتر وبسایت ReactRouter.com هست که واقعا نسبت به نسخه 5 خیلی بهتر شده.
در این نسخه همچنان همون BrowserRouter رو داریم و تغییری نکرده.
=> اما در این نسخه خبری از Switch نیست، به جای اون کامپوننت Routes رو داریم، که داخل BrowserRouter قرار میگیره و همه Route ها حتما باید داخل این Routes قرار بگیرن.
=> در این نسخه در <Route> ،خبری از props های component و render نیست، کامپوننتی که باید رندر بشه رو به صورت JSX به element میدیم.
اگر به صورت element={HomePage} بنویسیم غلطه، حتما باید به صورت JSX یعنی </HomePage> باشه.
=> در نسخه 5 ما باید Route ها رو به یه ترتیب خاصی(جزء به کل) قرار میدادیم تا به درستی رندر بشه، اما در این نسخه، ترتیب Route ها مهم نیست و خودش اونی که دقیق تره و بیشتر با آدرس match میشه رو انتخاب میکنه.
مثلا اگر آدرس /teams/new باشه، به هر دو مسیر زیر میخوره:
اما ریکت روتر همون teams/new رو انتخاب میکنه، چون دقیق تر از /teams/:teamId هست و بیشتر match میشه. در نسخه قبلی حتما باید ترتیب روت ها برعکس میبود تا درست رندر میشد.
=> با توجه به نکته بالا احتمالا متوجه شدید که در این نسخه دیگه خبری از exact نیست. ریکتروتر اونقدر درک و فهم داره که اون مسیری که دقیقتر match بشه رو انتخاب کنه.
اما یه نکته:
اگر بخوایم اون ساختار قبلی رو داشته باشیم که اگر URL با مقدار path ای که به <Route> دادیم شروع شده بود، یعنی اگر url.startsWith(path) مقدارش true بود، Route انتخاب بشه و رندر بشه، چیکار کنیم؟
کافیه از “*” در انتهای path استفاده کنیم.
در این حالت، اگر بعد از /profile هر چیزی داشته باشیم، مثلا /profile/abcd/13 ، باز هم کامپوننت ProfilePage رندر میشه.
همچنین توجه کنید که این "*" فقط در انتهای path قابل قبوله و حتما بعد از / میاد، در نتیجه دو تا ساختار زیر معتبر نیستن:
/files/*/cat.jpg
/files-*
در این نسخه، همچنان در خدمت <Link> هستیم برای این منظور که لینک هایی داشته باشیم و با کلیک کردن روی اونها وارد مسیر دیگهای بشیم.
برای imperative navigation یا programmatic navigation یعنی همون حالتی که بخوایم مثلا بعد از اینکه یک event رخ داد، یک فرم submit شد، یک درخواست ajax انجام شد و... وارد یک روت دیگه بشیم، در نسخه 5 از history.push یا history.replace استفاده میکردیم.
=> اما در این نسخه خبری از history نیست. اینجا از هوک useNavigate استفاده میکنیم.
این حالت کار همون history.push رو انجام میده، اما برای history.replace چی داریم؟
تابع navigate به عنوان آرگومان دوم یک شیء میگیره و یک ویژگی replace که مقدارش رو true میدیم. با اینکار یک مسیری رو replace میکنیم، یعنی مسیر فعلی رو جایگزین کنیم نه اینکه مسیر جدیدی رو به session history اضافه کنیم، در این حالت با دکمه برگشت مرورگر، نمیتونیم برگردیم به مسیر قبلی.
در لینک های بالای سایت یا همون nav links، وقتی روی یک لینک کلیک میکنیم و وارد صفحهش میشیم، معمولا میخوایم اون لینک(اون لینکی که فعال یا active هست) یک استایل خاص بگیره، برای این کار از <NavLink> استفاده میکنیم.
=> در این نسخه خبری از ویژگی های activeClassName و activeStyle نیست. به جای اونها، className و style یک تابع میگیرن که ورودیش یک شیء با ویژگی isActive هست، براساس true یا false بودن این مقدار میتونیم یک style obj یا class name رو return کنیم تا لینکِ فعال، ظاهر متفاوت داشته باشه.
اگر URL ،برابرِ مقدار to ِ این <NavLink> باشه، isActive مقدارش true هست، در غیر این صورت، false میشه.
اون <Link> هایی که مقدار to اونها با / شروع نمیشه، آدرسشون نسبی هست، نسبت به مسیری که در اون قرار دارن و اون مسیر به صورت خودکار به ابتدای آدرس اونها اضافه میشه:
در مثال بالا دو تا لینک داخل کامپوننت Dashboard داریم که آدرسشون نسبی هست(چون با / شروع نشدن) کامپوننت Dashboard در مسیر /dashboard رندر میشه، پس این دو تا لینک مسیرشون میشه
/dashboard/invoices و /dashboard/team ، چون داخل کامپوننت Dashboard قرار دارن.
=> این قابلیت خیلی خوبه چون اگر url ِ اون parent route رو یه روزی تغییر بکنه، اینها هم به صورت خودکار تغییر میکنن. مثلا اگر آدرسی که کامپوننت Dashboard در اون رندر میشه رو به /dash تغییر بدیم، اون دو تا لینک هم به /dash/invoices و /dash/team تغییر میکنن.
در نسخه 5 برای redirect کردن از کامپوننت <Redirect> داخل Route استفاده میکردیم:
=> در این نسخه از کامپوننت <Navigate> استفاده میکنیم و اون رو به عنوان element به Route میدیم و مسیری که میخوایم به اون redirect بشه رو به ویژگی to اون میدیم:
یعنی اگر کاربر مسیر /blogsPage رو وارد کرد، به صورت خودکار به /blogs هدایت میشه و url به /blogs تغییر میکنه.
در این حالت با اضافه کردن ویژگی replace ،بهش میگیم این مسیر جدید رو جایگزین اون چه که کاربر وارد کرده کن، اگر اون رو اضافه نکنیم، یک entry جدید به session history اضافه میشه و البته دکمه برگشت به عقب مرورگر هم به یه مشکل ریزی ممکنه بربخوره و کار نکنه:)
در این نسخه <Route> ها میتونن داخل هم قرار بگیرن و path هاشون به صورت نسبی و ادامه path ای که Route پدرشون داره در نظر گرفته میشه. یعنی چی؟
در نسخه قبلی وقتی مثلا یک صفحه /blogs داشتیم و میخواستیم یک مسیر دیگه به صورت /blogs/:blogId داشته باشیم که یک کامپوننت دیگه داخل کامپوننت پدر رندر میشد، یک Route دیگه داخل خود کامپوننت صفحه /blogs ایجاد میکردیم که مثلا جزئیات پست داخل اون رندر بشه، اما اینجا کافیه اون <Route> رو جایی که داریم روت ها رو تعریف میکنیم، داخل <Route> ِپدرش قرار بدیم.
گفتیم که path نسبی هست، یعنی آدرس parent route به صورت خودکار به ابتدای child route اضافه میشه و نباید /blogs رو بنویسیم، فقط قسمت :blogId رو مینویسیم، بدون / قبلش :
در این حالت، وقتی URL مثلا /blogs/1234 هست، component tree و اونچه که در خروجی داریم،چنین چیزی هست:
و این یعنی داخل کامپوننت BlogPage باید کامپوننت Blog رو رندر کنیم. ولی خب چطور مشخص کنیم کامپوننت Blog در کدوم قسمت BlogPage باید رندر بشه؟
=> در این نسخه با کامپوننت Outlet این کار رو میکنیم. وقتی url با مسیر Child Route یکی بشه، هر جا که Outlet قرار گرفته باشه، nested route مورد نظر رندر میشه:
در کد بالا، وقتی URL مثلا /blogs/1234 باشه، کامپوننت Blog بعد از <h2>Blogs Page</h2> رندر میشه.
یک مثال دیگه از مستندات خود ریکتروتر:
پایان قسمت اول
امیدوارم مفید بوده باشه
=> قسمت دوم