دریافت پارامترهای مسیریابی
گاهی از اوقات نیاز به ارسال پارامترهایی به مسیریابیهای تعریف شدهاست. برای مثال در لیست محصولات تعریف شده، بسته به انتخاب هر کدام، باید id متناظری نیز در URL نهایی ظاهر شود. به این Id، یک Route parameter گفته میشود. برای پیاده سازی این نیازمندی به صورت زیر عمل میکنیم:
در فایل app.js، یک مسیریابی جدید را برای نمایش جزئیات یک محصول اضافه میکنیم:
import ProductDetails from "./components/productDetails"; // ... class App extends Component { render() { return ( <div> <NavBar /> <div className="container"> <Switch> <Route path="/products/:id" component={ProductDetails} /> <Route path="/products" render={props => ( <Products param1="123" param2="456" {...props} /> )} /> <Route path="/posts/:year/:month" component={Posts} /> <Route path="/admin" component={Dashboard} /> <Route path="/" component={Home} /> </Switch> </div> </div> ); } }
کامپوننت جدید src\components\productDetails.jsx نیز به صورت زیر تعریف شدهاست:
import React, { Component } from "react"; class ProductDetails extends Component { handleSave = () => { // Navigate to /products }; render() { return ( <div> <h1>Product Details - </h1> <button className="btn btn-primary" onClick={this.handleSave}> </div> ); } } export default ProductDetails;
در اینجا با گشودن اطلاعات خاصیت match تزریق شدهی به کامپوننت ProductDetails، میتوان اطلاعاتی مانند پارامترهای دریافتی مسیریابی را دقیقا مشاهده کرد. برای مثال در این تصویر id=1 از URL بالای صفحه که به http://localhost:3000/products/1 تنظیم شدهاست، دریافت میشود.
بنابراین امکان خواندن اطلاعات پارامترهای مسیریابی، توسط شیء match تزریق شدهی به یک کامپوننت وجود دارد. به همین جهت کامپوننت ProductDetails را ویرایش کرده و المان h1 آنرا جهت نمایش id محصول به صورت زیر تغییر میدهیم که در آن شیء match.params، از props کامپوننت تامین میشود:
<h1>Product Details - {this.props.match.params.id} </h1>
برای آزمایش آن مجددا از صفحهی products شروع کرده و بر روی لینک یکی از محصولات، کلیک کنید. در اینجا هرچند id محصول به درستی نمایش داده میشود، اما ... نمایش جزئیات آن به همراه بارگذاری کامل و مجدد صفحهی آن است که از حالت SPA خارج شدهاست. برای رفع این مشکل به کامپوننت products مراجعه کرده و anchorهای تعریف شده را همانطور که در قسمت قبل نیز بررسی کردیم، تبدیل به کامپوننت Link میکنیم.
از حالت قبلی:
{this.state.products.map(product => ( <li key={product.id}> <a href={`/products/${product.id}`}>{product.name}</a> </li> ))}
import { Link } from "react-router-dom"; // ... <Link to={`/products/${product.id}`}>{product.name}</Link>
پارامترهای اختیاری مسیریابی
به تعریف مسیریابی زیر، دو پارامتر سال و ماه، اضافه شدهاند:
<Route path="/posts/:year/:month" component={Posts} />
برای رفع این مشکل میتوان با اضافه کردن یک ? به هر پارامتر، پارامترهای تعریف شده را اختیاری کرد:
<Route path="/posts/:year?/:month?" component={Posts} />
با این تغییرات اگر مجددا آدرس http://localhost:3000/posts/2018 را درخواست کنیم، کامپوننت Posts بجای کامپوننت Home نمایش داده میشود.
اکنون کامپوننت Posts را به صورت زیر تغییر میدهیم تا پارامترهای مسیریابی را نیز درج کند:
import React from "react"; const Posts = ({ match }) => { return ( <div> <h1>Posts</h1> Year: {match.params.year} , Month: {match.params.month} </div> ); }; export default Posts;
پس از ذخیره سازی این تغییرات و بارگذاری مجدد برنامه در مرورگر، اگر آدرس http://localhost:3000/posts/2018/1 را وارد کنیم، خروجی زیر حاصل میشود:
کار با پارامترهای کوئری استرینگهای مسیریابی
پارامترهای اختیاری، جزو قابلیتهایی هستند که باید تا حد ممکن از بکارگیری آنها اجتناب و آنها را با کوئری استرینگها تعریف کرد. کوئری استرینگها با یک ? در انتهای URL شروع میشوند و میتوانند چندین پارامتر را داشته باشند؛ مانند: http://localhost:3000/posts?sortBy=newest&approved=true و یا حتی میتوان آنها را با پارامترهای اختیاری نیز ترکیب کرد مانند: http://localhost:3000/posts/2018/05?sortBy=newest&approved=true
برای استخراج کوئری استرینگها در برنامههای React باید از شیء location استفاده کرد:
در اینجا مقدار خاصیت search، کل قسمت کوئری استرینگها را به همراه دارد. البته ما قصد پردازش آنرا به صورت دستی نداریم. به همین جهت از کتابخانهی زیر برای انجام اینکار استفاده خواهیم کرد:
> npm i query-string --save
import queryString from "query-string"; const Posts = ({ match, location }) => { const result = queryString.parse(location.search); console.log(result); // ...
- سپس شیء queryString را از ماژول مرتبط، import میکنیم. در ادامه به کمک متد parse آن، میتوان location.search را آنالیز کرد که خروجی آن، یک شیء جاوا اسکریپتی به صورت زیر است:
{approved: "true", sortBy: "newest"}
const { approved, sortBy } = queryString.parse(location.search);
یک نکته: باید دقت داشت که کتابخانهی query-string، همیشه مقادیر خواص را رشتهای بازگشت میدهد؛ حتی اگر عدد باشند.
مدیریت مسیرهای نامعتبر درخواستی
فرض کنید کاربری آدرس غیرمعتبر http://localhost:3000/xyz را که هیچ نوع مسیریابی را برای آن تعریف نکردهایم، درخواست میکند. در این حالت برنامه کامپوننت home را رندر میکند که مرتبط است با تعاریف مسیریابی برنامه در فایل app.js. تنها path تعریف شدهای که با این آدرس تطابق پیدا میکند، / متناظر با کامپوننت home است.
بجای این رفتار پیشفرض، مایل هستیم که کاربر به یک صفحهی سفارشی «پیدا نشد» هدایت شود. به همین جهت ابتدا کامپوننت جدید تابعی بدون حالت src\components\notFound.jsx را با محتوای زیر ایجاد میکنیم:
import React from "react"; const NotFound = () => { return <h1>Not Found</h1>; }; export default NotFound;
<Route path="/" exact component={Home} />
- ابتدا شیء Redirect را از react-router-dom باید import کنیم.
- همچنین import کامپوننت NotFound نیز باید ذکر شود.
- سپس پیش از مسیریابی کلی /، مسیریابی جدید not-found را که به کامپوننت NotFound اشاره میکند، اضافه میکنیم.
- اکنون در انتهای Switch تعریف شده (جائی که دیگر هیچ مسیریابی تعریف شدهای، با مسیر درخواستی کاربر، تطابق نداشته)، باید کامپوننت Redirect را جهت هدایت به این مسیریابی جدید، تعریف کرد:
import { Redirect, Route, Switch } from "react-router-dom"; //... import NotFound from "./components/notFound"; //... class App extends Component { render() { return ( <div> <NavBar /> <div className="container"> <Switch> //... <Route path="/not-found" component={NotFound} /> <Route path="/" exact component={Home} /> <Redirect to="/not-found" /> </Switch> </div> </div> ); } }
کاربرد دیگر کامپوننت Redirect، هدایت کاربران از یک آدرس قدیمی، به یک آدرس جدید است که نحوهی تعریف آن به صورت زیر میباشد:
<Redirect from="/messages" to="/posts" />
هدایت کاربران به آدرسهای مختلف با برنامه نویسی
گاهی از اوقات پس از تکمیل فرمی و یا کلیک بر روی دکمهای، میخواهیم کاربر را به آدرس خاصی هدایت کنیم. برای مثال در برنامهی جاری میخواهیم زمانیکه کاربری صفحهی جزئیات یک محصول را مشاهده و بر روی دکمهی فرضی Save کلیک کرد، دوباره به همان صفحهی لیست محصولات هدایت شود؛ برای این منظور از شیء history استفاده خواهیم کرد:
در اینجا متدها و خواص شیء history را مشاهده میکنید. برای نمونه توسط متد push آن میتوان آدرس جدیدی را به تاریخچهی آدرسهای مرور شدهی توسط کاربر، اضافه کرد و کاربر را با برنامه نویسی، به صفحهی جدیدی هدایت نمود:
class ProductDetails extends Component { handleSave = () => { // Navigate to /products this.props.history.push("/products"); };
یک نکته: اگر به تصویر دقت کنید، متد replace هم در اینجا قابل استفاده است. متد push با افزودن رکوردی به تاریخچهی آدرسهای مرور شدهی در مرورگر، امکان بازگشت به محل قبلی را با کلیک بر روی دکمهی back مرورگر، فراهم میکند؛ اما replace تنها رکورد آدرس جاری را در تاریخچهی مرورگر به روز رسانی میکند. یعنی از داشتن تاریخچه محروم خواهیم شد. عمدهی کاربرد این متد، در صفحات لاگین است؛ زمانیکه کاربر به سیستم وارد میشود و به صفحهی جدیدی مراجعه میکند، با کلیک بر روی دکمهی back، دوباره نمیخواهیم او را به صفحهی لاگین هدایت کنیم.
تعریف مسیریابیهای تو در تو
قصد داریم به صفحهی admin، دو لینک جدید به مطالب و کاربران ادمین را اضافه کنیم، به نحوی که با کلیک بر روی هر کدام، محتوای هر صفحهی متناظر، در همینجا نمایش داده شود (تصویر فوق). به عبارتی در حال حاضر، مسیریابی تعریف شده، جهت مدیریت لینکهای NavBar بالای صفحه کار میکند. اکنون میخواهیم مسیریابی دیگری را داخل آن برای پوشش منوی کنار صفحهی ادمین اضافه کنیم. به اینکار، تعریف مسیریابیهای تو در تو گفته میشود و پیاده سازی آن توسط کامپوننت react-router-dom بسیار سادهاست و شامل این موارد میشود:
- ابتدا مسیریابیهای جدید را همینجا داخل کامپوننت src\components\admin\dashboard.jsx تعریف میکنیم:
const Dashboard = ({ match }) => { return ( <div> <h1>Admin Dashboard</h1> <div className="row"> <div className="col-3"> <SideBar /> </div> <div className="col"> <Route path="/admin/users" component={Users} /> <Route path="/admin/posts" component={Posts} /> </div> </div> </div> ); };
تنها نکتهی جدید آن، امکان درج کامپوننت Route در قسمتهای مختلف برنامه، خارج از app.js میباشد و این امکان محدود به app.js نیست. در این حالت اگر مسیر /admin/posts توسط کاربر وارد شد، دقیقا در همان محلی که کامپوننت Route درج شدهاست، کامپوننت متناظر با این مسیر یعنی کامپوننت Posts، رندر میشود.
در ادامه محتوای این کامپوننتهای جدید را نیز ملاحظه میکنید:
محتوای کامپوننت src\components\admin\sidebar.jsx
import React from "react"; import { Link } from "react-router-dom"; const SideBar = () => { return ( <ul className="list-group"> <li className="list-group-item"> <Link to="/admin/posts">Posts</Link> </li> <li className="list-group-item"> <Link to="/admin/users">Users</Link> </li> </ul> ); }; export default SideBar;
محتوای کامپوننت src\components\admin\users.jsx
import React from "react"; const Users = () => { return <h1>Admin Users</h1>; }; export default Users;
محتوای کامپوننت src\components\admin\posts.jsx
import React from "react"; const Posts = () => { return ( <div> <h1>Admin Posts</h1> </div> ); }; export default Posts;
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-15-part-02.zip