تغییرات ریاکت روتر ۶ و ویژگی های جدید React Router

با سلام خدمت شما کاربران گرامی آکادمی راهکارینو، با یک مقاله دیگه در زمینه Reactjs در خدمتتون هستیم. در این مقاله قصد داریم کتابخانه ریکت روتر رو معرفی کنیم و ویژگی های جدید React-Router-6 رو نسبت به ریکت روتر 5 بررسی کنیم. همراه ما باشید…
نکته: اگر هم اکنون از نسخه 5 کتابخانه ری اکت روتر در پروژه خود استفاده می کنید و قصد آپدیت آن به نسخه 6 را دارید، می توانید به وب سایت مرجع React Router مراجعه کنید و مراحل کار را طبق این آموزش انجام دهید.
این مقاله در ژانویه 2023 به روز رسانی شده است و موضوع “تعریف protected routes” در بخش آخر مقاله مطرح شده است.
ریکت روتر (React Router) چیست؟
ری اکت روتر یک پکیج مسیریابی (Routing) برای کتابخانه ری اکت می باشد. از پکیج React-Router می توان در هر اپلیکیشن ریکتی استفاده کرد. هم سمت سرور (با Node.js) و هم سمت کلاینت (با React.js یا React-Native).
برخی از تغییرات ریکت روتر 6 و ویژگی های جدید آن:
در نسخه 6 کتابخانه React Router تغییرات جذابی رخ داده است و همچنین امکانات جدیدی به این کتابخانه محبوب اضافه شده است. در ادامه مقاله به معرفی و بررسی این ویژگی ها می پردازیم.
1) جایگزینی کامپوننت Switch با Routes:
اگر تا بحال از پکیج react router در پروژه تان استفاده کرده باشید حتما می دانید که باید route های اپلیکیشن خود را در کامپوننت Switch تعریف کنید. بعنوان مثال:
export function App() { return ( <div> <Switch> <Route path="/about"> <AboutPage /> </Route> <Route exact path="/profile"> <ProfilePage /> </Route> <Route path="/profile/:id"> <ProfileUserPage /> </Route> </Switch> </div> ) }
و حالا در ورژن 6 ریکت روتر بجای کامپوننت Switch باید از Routes استفاده کنیم و همچنین در هر مسیر یا Route یک props داریم بنام element که برابر با نام کامپوننت موردنظر خواهد بود. به مثال زیر توجه کنید:
export function App() { return ( <div> <Routes> <Route path="/about" element={<AboutPage />} /> <Route exact path="/profile" element={<ProfilePage />} /> <Route path="/profile/:id" element={<ProfileUserPage />} /> </Routes> </div> ) }
همانطور که مشاهده می کنید، در نسخه 6 بجای اینکه کامپوننت را بصورت children در داخل تگ باز و بسته Route تعریف کنیم، آن را به element نسبت می دهیم.
2) عدم نیاز به تعریف کلمه exact:
در نسخه جدید ریکت روتر تغییراتی در ارزیابی مسیرها و نمایش کامپوننت مرتبط ایجاد شده است. همانطور که می دانید در ریکت روتر نسخه 5 برای اینکه دقیقا اعلام کنیم در یک مسیر مشخص کدام کامپوننت لود شود، باید از کلمه کلیدی exact در Route استفاده می کردیم. بصورت زیر:
export function App() { return ( <div> <Switch> <Route path="/about"> <AboutPage /> </Route> <Route exact path="/profile"> <ProfilePage /> </Route> <Route path="/profile/:id"> <ProfileUserPage /> </Route> </Switch> </div> ) }
در مثال بالا اگر در Route دوم کلمه exact را تعریف نمی کردیم، در تمام مسیرهایی که با آدرس profile/ شروع شود کامپوننت ProfilePage لود می شود. اما در ریکت روتر ورژن 6 این مشکل حل شده و دیگر نیازی به تعریف exact نمی باشد. زیرا ریکت روتر بطور پیش فرض Route ها را exact در نظر می گیرد و فقط در مسیر تعریف شده کامپوننت موردنظر را لود می کند.
به مثال زیر توجه کنید:
export function App() { return ( <div> <Routes> <Route path="/about" element={<AboutPage />} /> <Route path="/profile" element={<ProfilePage />} /> <Route path="/profile/:id" element={<ProfileUserPage />} /> </Routes> </div> ) }
3) تعریف متفاوت کلاس اکتیو در کامپوننت NavLink:
در ورژن 5 هنگام استفاده از NavLink برای مشخص کردن لینک فعال در صفحات، از activeClassName استفاده می کردیم. بعنوان مثال در کد زیر تعریف کردیم که لینک اول در مسیر about/ کلاس active را بگیرد. در واقع فعال شود.
export function Header() { return ( <header> <ul> <li> <NavLink activeClassName="active" to="/about" /> </li> <li> <NavLink activeClassName="active" to="/profile" /> </li> </ul> </header> ) }
اما در ورژن 6 ریکت روتر، خودمان باید بصورت دستی این قضیه را مدیریت کنیم. به مثال زیر توجه کنید:
export function Header() { return ( <header> <ul> <li> <NavLink className={(navData) => navData.isActive ? "active" : "" } to="/about" /> </li> <li> <NavLink className={(navData) => navData.isActive ? "active" : "" } to="/profile" /> </li> </ul> </header> ) }
مشاهده می کنید که برای تعریف کلاس اکتیو در NavLink باید یک فانکشن به آن پاس دهیم. navData یک آبجکت است که یکی از property های آن isActive می باشد.
4) استفاده از useNavigate بجای useHistory:
در ورژن 5 برای جابجایی بین صفحات، می توانستیم از هوک useHistory استفاده کنیم و متد push را فراخوانی کنیم. به شکل زیر:
// This is a React Router v5 app import { useHistory } from "react-router-dom"; function App() { let history = useHistory(); function handleClick() { history.push("/home"); } return ( <div> <button onClick={handleClick}>go home</button> </div> ); }
اما در ورژن 6 هوک useNavigate جایگزین آن شد و متد push ندارد. به صورت زیر تعریف می شود:
// This is a React Router v6 app import { useNavigate } from "react-router-dom"; function App() { let navigate = useNavigate(); function handleClick() { navigate("/home"); } return ( <div> <button onClick={handleClick}>go home</button> </div> ); }
5) مسیرهای تو در تو (nested routes):
در اپلیکیشن های بزرگ، به منظور خوانایی هر چه بیشتر کدها و همچنین مقیاس پذیری بالا، بهتر است مسیرهای اپلیکیشن را در چندین المنت Routes مختلف تعریف کنیم اما در اپلیکیشن های کوچک و غیر تجاری، برای اینکه تمام مسیرها را در یک فایل تعریف کنیم می توانیم از قابلیت مسیرهای تودرتو یا Nested Routes در ریکت روتر 6 استفاده کرد.
بعنوان مثال، فرض کنید می خواهیم مسیرهای user/profile و user/account را در زیرمجموعه مسیر user/ ایجاد کنیم. به شکل زیر:
در ری اکت روتر 6 کد زیر را خواهیم داشت:
const App = () => { return ( <> <h1>React Router</h1> <nav> <Link to="/home">Home</Link> <Link to="/user">User</Link> </nav> <Routes> <Route index element={<Home />} /> <Route path="home" element={<Home />} /> <Route path="user" element={<User />}> <Route path="profile" element={<Profile />} /> <Route path="account" element={<Account />} /> </Route> <Route path="*" element={<NoMatch />} /> </Routes> </> ); };
اما اگر کد بالا را اجرا کنیم خواهیم دید که فقط مسیر user/ بدرستی کار می کند و کامپوننت <User> لود می شود. مسیرهای تودرتو یعنی profile/ و account/ مشکل دارند و مجددا کامپوننت user را لود می کنند. برای رفع این مشکل، باید از کامپوننت Outlet در ریکت روتر 6 استفاده کرد. به صورت زیر:
import { Routes, Route, Link, Outlet } from 'react-router-dom'; ... const User = () => { return ( <> <h1>User</h1> <nav> <Link to="profile">Profile</Link> <Link to="account">Account</Link> </nav> <Outlet /> </> ); };
کار Outlet اینست که کامپوننت فرزند مرتبط با مسیر تو در تو را لود کند. در این مثال کامپوننت های Profile و Account در مسیرهای profile/ و account/ لود خواهند شد. به تصویر زیر توجه کنید:
6) حذف <Redirect> داخل کامپوننت <Switch>:
برای تغییر مسیر یا redirect یک مسیر، در نسخه 6 ریکت روتر، نمی توان از کامپوننت Redirect به شکل مستقیم داخل Switch استفاده کرد. بلکه باید بصورت render prop تعریف شود. به این شکل:
// Change this: <Switch> <Redirect from="about" to="about-us" /> </Switch> // to this: <Switch> <Route path="about" render={() => <Redirect to="about-us" />} /> </Switch>
7) کاهش سایز فایل bundle نهایی:
پس از تست و ارزیابی حجم فایل های bundle شده در کتابخانه ریکت روتر و مقایسه نسخه 5 با 6 متوجه می شویم که سایز bundle ریکت روتر 6 حدود 70 درصد کمتر از نسخه 5 می باشد:
در تصویر بالا، سایز bundle ریکت روتر 5 (react-router-dom@5.1.2) با ریکت روتر 6 (react-router-dom@6.0.0-alpha.2) مقایسه شده است. شاهد تفاوت بسیار زیاد آنها می باشیم. سایز کمتر bundle خروجی در نسخه 6 حاکی از این است که سرعت لود اپلیکیشن بالاتر خواهد بود.
8) تعریف مسیر محافظت شده (Protected Route):
به منظور تعریف یک مسیر protected در اپلیکیشن ری اکت، روش های مختلفی وجود دارد. مسیر ایمن یا محافظت شده مسیری است که کاربر برای دسترسی به آن نیاز به لاگین و توکن (access-token) دارد. برای ایجاد یک مسیر ایمن و محافظت شده در نسخه 5 از کتابخانه react-router-dom کافی بود یک کامپوننت سفارشی، مثلا بنام ProtectedRoute بسازیم و به شکل زیر، مسیرهای موردنظر را در کامپوننت App تعریف کنیم:
<Router> <ProtectedRoute component={UserPanel} path="/panel" /> <Route component={Login} path="/login" /> </Router>
و سورس کد کامپوننت ProtectedRoute هم شبیه کد زیر می باشد:
export const ProtectedRoute = ({children, ...rest}) => { let hasToken = true; return ( <Route {...rest}> {hasToken ? children : <Redirect to="/login" /> } </Route> ) }
اما در نسخه 6 لایبرری React Router Dom قضیه کمی متفاوت است. تغییراتی باید در کدهای فوق اعمال کنیم.
به این منظور، ابتدا باید تغییرات زیر را در مسیرهای کامپوننت App انجام دهیم:
<Router> <Routes> <ProtectedRoute element={<UserPanel />} path="/panel" /> <Route element={<Login />} path="/login" /> </Routes> </Router>
سپس در کامپوننت ProtectedRoute کدهای زیر را خواهیم داشت:
import {Outlet, Navigate} from "react-router-dom"; export const ProtectedRoute = () => { let hasToken = true; return ( {hasToken ? <Outlet /> : <Navigate to="/login" /> } ) }
مشاهده می کنید که کامپوننت Redirect در ورژن 6 به Navigate تغییرنام داده و همچنین برای اینکه یک route سفارشی در زیرمجموعه Routes داشته باشیم باید از Outlet استفاده کنیم.
دوستان عزیز، مقاله آموزشی “تفاوت های ریکت روتر 6 با نسخه 5 و ویژگی های جدید React Router 6” در اینجا به پایان می رسد. امیدوارم استفاده لازم را برده باشید. اگر سوال یا پیشنهادی درباره موضوع مقاله دارید می توانید از طریق فرم ارسال دیدگاه پایین مقاله اقدام کنید.
مطالب زیر را حتما مطالعه کنید
4 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
واقعا دست مریزاد خسته نباشید بابت مطلب مفیدتون
سلامت باشی دوست عزیز.
امروز سرکار توی پروژه یهو با Outlet برخورد کردم و کلی فک کردم و گفتم این دیگه چیه. اومدم خونه داشتم سرچ میکردم که مطالب مفید و خلاصه شما رو دیدم. مرسی.
(امروز آخرش همکارم گفت بی خیال شو هر جور شده تسک امروز رو بزن 😅😅😅)
سلام محمد عزیز.
خداروشکر که مقاله براتون مفید بوده.