تخفیف ویژه برای همه دوره‌ها از شنبه

آموزش استفاده از React Router

React Router محبوب‌ترین کتابخانه مسیریابی در React است اما استفاده از آن در کنار برخی از ویژگی‌ها ممکن است کمی پییچده‌تر باشد. در این مقاله قصد داریم تا همه مواردی که باید در مورد React Router بدانیم را باهم بررسی کنیم تا بتوانیم از پیشرفته‌ترین ویژگی‌ها هم به راحتی استفاده کنیم.

مفاهیم اولیه React Router

قبل از شروع به بررسی ویژگی‌های پیشرفته React Router ابتدا می‌خواهیم در مورد اصول اولیه آن صحبت کنیم. برای استفاده از React Router در وب، باید

npm i react-router-dom
npm i react-router-dom را برای نصب آن اجرا کنیم. این کتابخانه به طور خاص نسخه DOM از React Router را نصب می‌کند. اگر از React Native استفاده می‌کنیم به جای آن باید
react-router-native
react-router-nativeرا نصب کنیم. به غیر از این تفاوت کوچک، کتابخانه‌ها تقریباً یکسان کار می‌کنند.

در این مقاله ما روی

react-router-dom
react-router-domتمرکز خواهیم کرد، اما همانطور که گفتیم هر دو این کتابخانه‌ها تقریباً یکسان هستند.

پس از نصب کتابخانه، برای استفاده از React Router باید سه کار انجام دهیم:

  1. راه‌اندازی Router
  2. تعریف مسیرها
  3. مدیریت navigation

پیکربندی Router

ساده‌ترین مرحله تا کنون راه‌اندازی Router است. تنها کاری که باید انجام دهیم این است که روتر خاصی که نیاز داریم (

BrowserRouter
BrowserRouterبرای وب و
NativeRouter
NativeRouterبرای موبایل) را وارد کرده و کل برنامه خود را در آن قرار دهیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react"
import ReactDOM from "react-dom/client"
import App from "./App"
import { BrowserRouter } from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
)
import React from "react" import ReactDOM from "react-dom/client" import App from "./App" import { BrowserRouter } from "react-router-dom" const root = ReactDOM.createRoot(document.getElementById("root")) root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> )
import React from "react"
import ReactDOM from "react-dom/client"
import App from "./App"
import { BrowserRouter } from "react-router-dom"

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)

به طور کلی، روتر خود را در صفحه

index.js
index.jsبرنامه وارد می‌کنیم و کامپوننت
App
Appرا داخل آن قرار می‌دهیم. روتر درست مانند یک context در React کار می‌کند و تمام اطلاعات لازم را در اختیار برنامه ما قرار می‌دهد تا بتوانیم مسیریابی را انجام داده و از تمام هوک‌های سفارشی React Router استفاده کنیم.

تعریف مسیرها

مرحله بعدی در React Router این است که مسیرهای خود را تعریف کنیم. این کار معمولاً در سطح بالاتر برنامه ما انجام می‌شود، مانند کامپوننت

App
App، اما می‌توانیم در هر جایی که بخواهیم این کار را انجام دهیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
export function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
</Routes>
)
}
import { Route, Routes } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" export function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> </Routes> ) }
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"

export function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/books" element={<BookList />} />
    </Routes>
  )
}

تعریف مسیرها به سادگی تعریف یک کامپوننت

Route
Routeبرای هر مسیر در برنامه خود و سپس قرار دادن تمام آن کامپوننت‌های
Route
Routeدر یک کامپوننت
Routes
Routesاست. زمانی که URL ما تغییر کند، React Router به مسیرهای تعریف شده در کامپوننت
Routes
Routes نگاه می‌کند و محتوای موجود در پراپ
element
elementمربوط به
Route
Route را که
path
pathمطابق با URL دارد رندر می‌کند. در مثال بالا اگر URL ما
/books
/booksباشد، در این صورت کامپوننت
BookList
BookListرندر می‌شود.

نکته خوبی که در مورد React Router وجود دارد این است که وقتی بین صفحات جابه‌جا می‌شویم، فقط محتوای داخل کامپوننت

Routes
Routes رفرش می‌شود. بقیه مطالب موجود در صفحه یکسان باقی خواهند ماند که این موضوع به عملکرد و تجربه کاربری(UX) بسیار کمک می‌کند.

مدیریت Navigation

آخرین مرحله برای React Router مدیریت navigation است. به طور معمول ما در یک اپلیکیشن با استفاده از تگ‌های anchor بین صفحات جابه‌جا می‌شویم، اما React Router از کامپوننت

Link
Linkسفارشی خود برای مدیریت navigation استفاده می‌کند. این کامپوننت فقط یک wrapper در اطراف یک تگ anchor است که برای اطمینان از اینکه همه مسیریابی‌ها و رندر‌های مجدد شرطی به درستی انجام می‌شود مورد استفاده قرار می‌گیرد. بنابراین می‌توانیم از آن همانند یک تگ anchor معمولی استفاده کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Route, Routes, Link } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
export function App() {
return (
<>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/books">Books</Link></li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
</Routes>
</>
)
}
import { Route, Routes, Link } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" export function App() { return ( <> <nav> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/books">Books</Link></li> </ul> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> </Routes> </> ) }
import { Route, Routes, Link } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"

export function App() {
  return (
    <>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/books">Books</Link></li>
        </ul>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/books" element={<BookList />} />
      </Routes>
    </>
  )
}

در این مثال ما دو لینک به صفحه home و books اضافه کردیم. همچنین نکته‌ای که وجود دارد این است که ما به جای پراپ

href
hrefکه با تگ anchor مورد استفاده قرار می‌گیرد، از پراپ
to
toبرای تنظیم URL استفاده کردیم. این تنها تفاوت بین کامپوننت
Link
Linkو تگ anchor است که باید آن را به خاطر بسپاریم زیرا این احتمال وجود دارد که این دو به اشتباه به‌جای هم استفاده شوند.

نکته دیگری که در مورد کد جدید باید به آن توجه داشته باشیم این است که navigationای که در بالای صفحه خود آن را می‌فرستیم خارج از کامپوننت

Routes
Routesاست، به این معنی که وقتی صفحه را تغییر می‌دهیم این بخش navigation دوباره رندر نمی‌شود زیرا فقط محتوای موجود در داخل کامپوننت
Routes
Routes است که با تغییر URL تغییر خواهد کرد.

تعاریف پیشرفته Route

اینجاست که React Router واقعا جالب می‌شود. موارد زیادی وجود دارد که می‌توانیم با کمک آن‌ها مسیریابی انجام دهیم تا مسیرهای پیچیده‌تر خوانایی بالاتر داشته و در کل بسیار کاربردی‌تر شوند. این کار را می‌توانیم از طریق پنج تکنیک اصلی انجام دهیم:

  1. مسیریابی پویا
  2. اولویت مسیریابی
  3. مسیرهای تودرتو
  4. مسیرهای چندگانه
  5. هوک
    useRoutes
    useRoutes

مسیریابی پویا

ساده‌ترین و رایج‌ترین ویژگی پیشرفته در React Router مدیریت مسیرهای پویا است. در مثالی که داریم فرض کنید که می‌خواهیم یک کامپوننت را برای هر Book به شکل منحصربفرد در برنامه خود ارائه دهیم. ما می‌توانیم هر یک از آن مسیرها را هاردکد کنیم اما اگر صدها کتاب داشته باشیم و یا اینکه کاربران توانایی ایجاد کتاب داشته باشند، هاردکد همه این مسیرها غیرممکن است. در چنین حالتی است که به یک مسیر پویا نیاز داریم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
<Route path="/books/:id" element={<Book />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BookList />} />
  <Route path="/books/:id" element={<Book />} />
</Routes>

مسیر نهایی در مثال بالا یک مسیر پویا است که دارای پارامتر پویا

:id
:idمی‌باشد. تعریف مسیرهای پویا در React Router به سادگی قرار دادن یک
:
:در مقابل هر چیزی است که می‌خواهیم قسمت پویا مسیر ما باشد. در مورد مثالی که ما داریم مسیر پویا با هر URLای که با
/book
/bookشروع می‌شود و با یک value به پایان می‌رسد مطابقت دارد. برای مثال، /
/books/1
/books/1،
/books/bookName
/books/bookNameو
/books/literally-anything
/books/literally-anythingهمگی با مسیر پویا ما مطابقت دارند.

تقریباً همیشه وقتی یک مسیر پویا مانند این مثال داریم و می‌خواهیم به مقدار پویا در کامپوننت سفارشی خود دسترسی پیدا کنیم جایی است که هوک

useParams
useParamsوارد عمل می‌شود.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useParams } from "react-router-dom"
export function Book() {
const { id } = useParams()
return (
<h1>Book {id}</h1>
)
}
import { useParams } from "react-router-dom" export function Book() { const { id } = useParams() return ( <h1>Book {id}</h1> ) }
import { useParams } from "react-router-dom"

export function Book() {
  const { id } = useParams()

  return (
    <h1>Book {id}</h1>
  )
}

هوک

useParams
useParams هیچ پارامتری را دریافت نمی‌کند و یک آبجکت با کلیدهایی را برمی‌گرداند که با پارامترهای پویا در مسیر ما مطابقت دارند. در مثال ما پارامتر پویا
:id
:idاست، بنابراین هوک
useParams
useParams یک آبجکت برمی‌گرداند که دارای یک کلید
id
idبوده و مقدار آن کلید، شناسه واقعی در URL خواهد بود. برای مثال، اگر URL ما
/books/3
/books/3بود، صفحه ما Book3 را رندر خواهد کرد.

اولویت مسیریابی

زمانی که ما فقط با مسیرهای دارای هاردکد سروکار داشته باشیم فهمیدن اینکه کدام مسیر رندر می‌شود بسیار آسان است، اما وقتی با مسیرهای پویا کار می‌کنیم این موضوع ممکن است کمی پیچیده‌تر باشد. برای مثال این مسیرها را در نظر بگیرید:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
<Route path="/books/:id" element={<Book />} />
<Route path="/books/new" element={<NewBook />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> <Route path="/books/new" element={<NewBook />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BookList />} />
  <Route path="/books/:id" element={<Book />} />
  <Route path="/books/new" element={<NewBook />} />
</Routes>

اگر ما URL تحت عنوان

/books/new
/books/newرا داشته باشیم در این صورت با کدام مسیر مطابقت دارد؟ از نظر فنی، ما دو مسیر
/books/:id
/books/:idو
/books/new
/books/newرا داریم که باهم مطابقت دارند زیرا مسیر پویا فقط فرض می‌کند که
new
newبخش
:id
:idدر URL است، بنابراین React Router به روش دیگری برای تعیین مسیری که باید رندر شود نیاز دارد.

در نسخه‌های قدیمی‌تر React Router، هر مسیری که در ابتدا تعریف شده باشد همان مسیری است که رندر می‌شود، بنابراین در مورد مثالی که داریم مسیر

/books/:id
/books/:idرندر خواهد شد که البته بدیهی است این چیزی نیست که ما می‌خواهیم. خوشبختانه نسخه ۶ React Router این را تغییر داد، در نتیجه اکنون React Router از یک الگوریتم برای تعیین مسیری که ما می‌خواهیم استفاده می‌کند. در مورد مثال ما بدیهی است که می‌خواهیم مسیر
/books/new
/books/new را رندر کنیم، بنابراین React Router آن مسیر را انتخاب می‌کند. روش کار این الگوریتم بسیار شبیه به ویژگی CSS است زیرا سعی می‌کند تعیین کند کدام مسیری که با URL ما مطابقت دارد خاص‌ترین است (کم‌ترین تعداد المنت پویا را دارد) و آن مسیر را انتخاب می‌کند.

در همین حین که درمورد اولویت مسیریابی صحبت می‌کنیم می‌خواهم چگونگی ایجاد مسیری که با هر چیزی مطابقت داشته باشد را هم باهم بررسی کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
<Route path="/books/:id" element={<Book />} />
<Route path="/books/new" element={<NewBook />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> <Route path="/books/new" element={<NewBook />} /> <Route path="*" element={<NotFound />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BookList />} />
  <Route path="/books/:id" element={<Book />} />
  <Route path="/books/new" element={<NewBook />} />
  <Route path="*" element={<NotFound />} />
</Routes>

یک

*
*با هر چیزی مطابقت دارد که آن را برای مواردی مانند صفحه ۴۰۴ به یک انتخاب عالی تبدیل می‌کند.

مسیرهای تودرتو

درنهایت در این بخش می‌خواهیم با نحوه مدیریت مسیرهای تودرتو آشنا شویم. در مثال بالا ما سه مسیر داریم که با

/books
/booksشروع می‌شوند، بنابراین می‌توانیم آن‌ها را به شکل تودرتو در داخل یکدیگر قرار دهیم تا مسیرهای تمیزتری داشته باشیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books">
<Route index element={<BookList />} />
<Route path=":id" element={<Book />} />
<Route path="new" element={<NewBook />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books">
    <Route index element={<BookList />} />
    <Route path=":id" element={<Book />} />
    <Route path="new" element={<NewBook />} />
  </Route>
  <Route path="*" element={<NotFound />} />
</Routes>

انجام این کار بسیار ساده است. تنها کار لازم این است که یک

Route
Route parent بسازیم که دارای پراپ
path
pathدر مسیر مشترک برای تمام کامپوننت‌های 
Route
Routechildها باشد. سپس می‌توانیم تمام کامپوننت‌های
Route
Routechild را در داخل
Route
Routeparent قرار دهیم. تنها تفاوت این است که پراپ
path
path کامپوننت‌های
Route
Routechildها دیگر شامل مسیر مشترک
/books
/booksنمی‌شوند. همچنین مسیری که برای
/books
/booksوجود داشت با یک کامپوننت
Route
Route جایگزین می‌شود که پراپ
path
path ندارد اما در عوض دارای یک پراپ
index
indexمی‌باشد. همه این موارد گویای این هستند که
Route
Routeایندکس با
Route
Route parent یکی است.

اگر از مسیرهای تودرتو فقط برای انجام این کار استفاده کنیم در این صورت به میزان کمی از مزایای آن بهره‌مند می‌شویم، اما قدرت واقعی مسیرهای تودرتو در نحوه مدیریت Layoutهای مشترک است.

Layout ‌های مشترک

تصور کنید که می‌خواهیم یک بخش nav را با لینک‌هایی به هر کتاب و همچنین لینک‌هایی به فرم کتاب جدید از هر یک از صفحات کتاب خود ارائه کنیم. برای انجام این کار معمولاً باید یک کامپوننت مشترک ایجاد کنیم تا این navigation ذخیره شود و سپس آن را در هر کامپوننت مرتبط با تک‌کتاب import کنیم. این راه کمی گیج‌کننده است، بنابراین React Router راه حل خود را برای این مشکل ایجاد کرده است. اگر یک پراپ

element
elementرا به یک مسیر parent ارسال کنیم، آن کامپوننت برای هر
Route
Routechild منفرد ارائه می‌شود، این بدان معناست که می‌توانیم به راحتی یک nav مشترک یا سایر کامپوننت‌های مشترک را در هر صفحه child قرار دهیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BooksLayout />}>
<Route index element={<BookList />} />
<Route path=":id" element={<Book />} />
<Route path="new" element={<NewBook />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BooksLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BooksLayout />}>
    <Route index element={<BookList />} />
    <Route path=":id" element={<Book />} />
    <Route path="new" element={<NewBook />} />
  </Route>
  <Route path="*" element={<NotFound />} />
</Routes>
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Link, Outlet } from "react-router-dom"
export function BooksLayout() {
return (
<>
<nav>
<ul>
<li><Link to="/books/1">Book 1</Link></li>
<li><Link to="/books/2">Book 2</Link></li>
<li><Link to="/books/new">New Book</Link></li>
</ul>
</nav>
<Outlet />
</>
)
}
import { Link, Outlet } from "react-router-dom" export function BooksLayout() { return ( <> <nav> <ul> <li><Link to="/books/1">Book 1</Link></li> <li><Link to="/books/2">Book 2</Link></li> <li><Link to="/books/new">New Book</Link></li> </ul> </nav> <Outlet /> </> ) }
import { Link, Outlet } from "react-router-dom"

export function BooksLayout() {
  return (
    <>
      <nav>
        <ul>
          <li><Link to="/books/1">Book 1</Link></li>
          <li><Link to="/books/2">Book 2</Link></li>
          <li><Link to="/books/new">New Book</Link></li>
        </ul>
      </nav>

      <Outlet />
    </>
  )
}

روشی که کد جدید ما کار خواهد کرد به این صورت است که هرگاه مسیری را در داخل

Route
Routeparent
/book
/bookمطابقت دهیم، کامپوننت
BooksLayout
BooksLayoutرا که شامل navigation مشترک ما است، رندر می‌کند. سپس هر
Route
Route childای که مطابقت داشته باشد، در هر جایی که کامپوننت
Outlet
Outletدر داخل کامپوننت layout قرار می‌گیرد، نمایش داده می‌شود. کامپوننت
Outlet
Outlet اساساً یک کامپوننت placeholder است که محتوای صفحه فعلی ما را رندر می‌کند. این ساختار خیلی مفید است و اشتراک‌گذاری کد بین مسیرها را بسیار آسان می‌کند.

اکنون آخرین روشی که می‌توانیم layoutها را با React Router به اشتراک بگذاریم این است که کامپوننت‌های

Route
Route child را داخل
Route
Route parent قرار دهیم که به جای پراپ
path
pathفقط یک پراپ
element
elementتعریف می‌کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BooksLayout />}>
<Route index element={<BookList />} />
<Route path=":id" element={<Book />} />
<Route path="new" element={<NewBook />} />
</Route>
<Route element={<OtherLayout />}>
<Route path="/contact" element={<Contact />} />
<Route path="/about" element={<About />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BooksLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route element={<OtherLayout />}> <Route path="/contact" element={<Contact />} /> <Route path="/about" element={<About />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BooksLayout />}>
    <Route index element={<BookList />} />
    <Route path=":id" element={<Book />} />
    <Route path="new" element={<NewBook />} />
  </Route>
  <Route element={<OtherLayout />}>
    <Route path="/contact" element={<Contact />} />
    <Route path="/about" element={<About />} />
  </Route>
  <Route path="*" element={<NotFound />} />
</Routes>

قسمت کد

<Route element={<OtherLayout />}>
<Route element={<OtherLayout />}>دو مسیر به نام‌های
/contact
/contactو
/about
/aboutایجاد می‌کند که هر دو در داخل کامپوننت
OtherLayout
OtherLayoutرندر می‌شوند. اگر بخواهیم آن مسیرها یک layout واحد را به اشتراک بگذارند، حتی اگر مسیر مشابهی نداشته باشند، این تکنیک قرار دادن چندین کامپوننت
Route
Routeدر یک کامپوننت
Route
Route parent بدون پراپ
path
pathمی‌تواند بسیار مفید باشد.

Outlet Context

آخرین نکته مهمی که باید در مورد کامپوننت‌های

Outlet
Outletبدانیم این است که آن‌ها می‌توانند از یک پراپ
context
contextاستفاده کنند که درست مانند context در React کار می‌کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Link, Outlet } from "react-router-dom"
export function BooksLayout() {
return (
<>
<nav>
<ul>
<li><Link to="/books/1">Book 1</Link></li>
<li><Link to="/books/2">Book 2</Link></li>
<li><Link to="/books/new">New Book</Link></li>
</ul>
</nav>
<Outlet context={{ hello: "world" }} />
</>
)
}
import { Link, Outlet } from "react-router-dom" export function BooksLayout() { return ( <> <nav> <ul> <li><Link to="/books/1">Book 1</Link></li> <li><Link to="/books/2">Book 2</Link></li> <li><Link to="/books/new">New Book</Link></li> </ul> </nav> <Outlet context={{ hello: "world" }} /> </> ) }
import { Link, Outlet } from "react-router-dom"

export function BooksLayout() {
  return (
    <>
      <nav>
        <ul>
          <li><Link to="/books/1">Book 1</Link></li>
          <li><Link to="/books/2">Book 2</Link></li>
          <li><Link to="/books/new">New Book</Link></li>
        </ul>
      </nav>

      <Outlet context={{ hello: "world" }} />
    </>
  )
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useParams, useOutletContext } from "react-router-dom"
export function Book() {
const { id } = useParams()
const context = useOutletContext()
return (
<h1>Book {id} {context.hello}</h1>
)
}
import { useParams, useOutletContext } from "react-router-dom" export function Book() { const { id } = useParams() const context = useOutletContext() return ( <h1>Book {id} {context.hello}</h1> ) }
import { useParams, useOutletContext } from "react-router-dom"

export function Book() {
  const { id } = useParams()
  const context = useOutletContext()

  return (
    <h1>Book {id} {context.hello}</h1>
  )
}

همانطور که در مثال بالا داریم، ما یک مقدار context تحت عنوان

{ hello: "world" }
{ hello: "world" }را ارسال کرده و سپس در کامپوننت child از هوک
useOutletContext
useOutletContextبرای دسترسی به مقدار context استفاده می‌کنیم. این یک الگوی بسیار رایج است زیرا اغلب ما داده‌های مشترکی را بین تمام کامپوننت‌های child خود خواهیم داشت که استفاده از context می‌تواند بهترین انتخاب برای این کار باشد.

مسیرهای چندگانه

یکی دیگر از کارهای فوق العاده قدرتمندی که می‌توانیم با React Router انجام دهیم، استفاده هم‌زمان از چندین کامپوننت

Routes
Routesاست. این کار را می‌توانیم به صورت دو کامپوننت جداگانه
Routes
Routes یا به عنوان
Routes
Routesهای تودرتو انجام دهیم.

Routesهای جداگانه

اگر می‌خواهیم دو بخش مختلف از محتوای برنامه را رندر کنیم که هر دو به URL برنامه وابسته هستند، به چندین کامپوننت

Routes
Routes نیاز داریم. یک مثال رایج برای این مفهوم به این صورت است که مثلا ما یک sidebar داریم که می‌خواهیم محتوای خاصی را برای URLهای خاص در آن رندر کنیم و همچنین یک صفحه اصلی که باید محتوای خاصی را براساس URL نشان دهد.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Route, Routes, Link } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { BookSidebar } from "./BookSidebar"
export function App() {
return (
<>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/books">Books</Link></li>
</ul>
</nav>
<aside>
<Routes>
<Route path="/books" element={<BookSidebar />}>
</Routes>
</aside>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<BookList />} />
</Routes>
</>
)
}
import { Route, Routes, Link } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" import { BookSidebar } from "./BookSidebar" export function App() { return ( <> <nav> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/books">Books</Link></li> </ul> </nav> <aside> <Routes> <Route path="/books" element={<BookSidebar />}> </Routes> </aside> <Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> </Routes> </> ) }
import { Route, Routes, Link } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { BookSidebar } from "./BookSidebar"

export function App() {
  return (
    <>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/books">Books</Link></li>
        </ul>
      </nav>

      <aside>
        <Routes>
          <Route path="/books" element={<BookSidebar />}>
        </Routes>
      </aside>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/books" element={<BookList />} />
      </Routes>
    </>
  )
}

در مثال بالا دو

Routes
Routes داریم.
Routes
Routes اصلی تمام کامپوننت‌های اصلی صفحه ما را تعریف می‌کند و سپس یک
Routes
Routes ثانویه در داخل
aside
asideداریم که وقتی در
/books
/booksهستیم، sidebar صفحه کتاب‌های ما را نمایش می‌دهد. این بدان معناست که اگر URL ما
/books
/booksباشد، هر دو کامپوننت
Routes
Routes محتوایی که دارند را رندر می‌کنند زیرا هر دو در مسیرهای خود مطابقت منحصربهفردی برای
/books
/books دارند.

یکی دیگر از کارهایی که می‌توانیم با چندین کامپوننت

Routes
Routes انجام دهیم، هاردکد کردن پراپ
location
locationاست.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes location="/books">
<Route path="/books" element={<BookSidebar />}>
</Routes>
<Routes location="/books"> <Route path="/books" element={<BookSidebar />}> </Routes>
<Routes location="/books">
  <Route path="/books" element={<BookSidebar />}>
</Routes>

با هاردکد کردن پراپ

location
locationرفتار پیش‌فرض یا React Router را نادیده می‌گیریم. بنابراین مهم نیست که URL صفحه ما چیست، این کامپوننت
Routes
Routesبا
Route
Routeخود مطابقت دارد، مثل این است که URL آن
/books
/booksمی‌باشد.

Routesهای تودرتو

راه دیگر برای استفاده از چندین کامپوننت

Routes
Routes این است که آن‌ها را در داخل یکدیگر قرار دهیم. اگر مسیرهای زیادی داریم و می‌خواهیم با انتقال مسیرهای مشابه به فایل‌های خود کدی که داریم را تمیزتر کنیم، این روش بسیار رایج است.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books/*" element={<BookRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Routes> <Route path="/" element={<Home />} /> <Route path="/books/*" element={<BookRoutes />} /> <Route path="*" element={<NotFound />} /> </Routes>
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books/*" element={<BookRoutes />} />
  <Route path="*" element={<NotFound />} />
</Routes>
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Routes, Route } from "react-router-dom"
import { BookList } from "./pages/BookList"
import { Book } from "./pages/Book"
import { NewBook } from "./pages/NewBook"
import { BookLayout } from "./BookLayout"
export function BookRoutes() {
return (
<Routes>
<Route element={<BookLayout />}>
<Route index element={<BookList />} />
<Route path=":id" element={<Book />} />
<Route path="new" element={<NewBook />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
)
}
import { Routes, Route } from "react-router-dom" import { BookList } from "./pages/BookList" import { Book } from "./pages/Book" import { NewBook } from "./pages/NewBook" import { BookLayout } from "./BookLayout" export function BookRoutes() { return ( <Routes> <Route element={<BookLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes> ) }
import { Routes, Route } from "react-router-dom"
import { BookList } from "./pages/BookList"
import { Book } from "./pages/Book"
import { NewBook } from "./pages/NewBook"
import { BookLayout } from "./BookLayout"

export function BookRoutes() {
  return (
    <Routes>
      <Route element={<BookLayout />}>
        <Route index element={<BookList />} />
        <Route path=":id" element={<Book />} />
        <Route path="new" element={<NewBook />} />
        <Route path="*" element={<NotFound />} />
      </Route>
    </Routes>
  )
}

Routes
Routesهای تودرتو در React Router بسیار ساده است. تنها کاری که باید انجام دهیم این است که یک کامپوننت جدید برای ذخیره
Routes
Routesهای تودرتو ایجاد کنیم، این کامپوننت باید یک کامپوننت
Routes
Routes داشته باشد و در داخل آن باید تمام کامپوننت‌های
Route
Routeای باشند که با
Route
Route parent مطابقت دارند. در مثالی که داریم تمام مسیرهای
/books
/booksخود را به کامپوننت
BookRoute
BookRouteمنتقل می‌کنیم. سپس در
Routes
Routes parent، باید
Route
Routeای را تعریف کنیم که
path
pathآن برابر با
path
pathای که همه
Routes
Routesهای تودرتو به اشتراک می‌گذارند، باشد. در مثال ما می‌تواند
/books
/books باشد. با این حال، نکته مهمی که باید به آن توجه کنیم این است که باید
path
path مربوط به
Route
Route parent خود را با یک
*
*پایان دهیم، در غیر این صورت مطابقت با مسیر childها به درستی صورت نمی‌گیرد.

اساساً، کدی که ما نوشته‌ایم می‌گوید زمانی که مسیری با

/book/
/book/شروع می‌شود، باید داخل کامپوننت
BookRoutes
BookRoutesجستجو کند تا ببیند آیا
Route
Routeای وجود دارد که با آن مطابقت داشته باشد یا خیر. همچنین یک مسیر
*
* دیگر در
BookRoutes
BookRoutes داریم تا مطمئن شویم که اگر URL ما با هیچ یک از
BookRoutes
BookRoutes مطابقت نداشته باشد، کامپوننت
NotFound
NotFoundبه درستی رندر خواهد شد.

هوک useRoutes

آخرین چیزی که باید در مورد تعریف مسیرها در React Router بدانیم این است که در صورت تمایل می‌توانیم برای تعریف مسیرهای خود به جای JSX از یک آبجکت جاوااسکریپت استفاده کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { Book } from "./Book"
export function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books">
<Route index element={<BookList />} />
<Route path=":id" element={<Book />} />
</Route>
</Routes>
)
}
import { Route, Routes } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" import { Book } from "./Book" export function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> </Route> </Routes> ) }
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { Book } from "./Book"

export function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/books">
        <Route index element={<BookList />} />
        <Route path=":id" element={<Book />} />
      </Route>
    </Routes>
  )
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { Book } from "./Book"
export function App() {
const element = useRoutes([
{
path: "/",
element: <Home />
},
{
path: "/books",
children: [
{ index: true, element: <BookList /> },
{ path: ":id", element: <Book /> }
]
}
])
return element
}
import { Route, Routes } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" import { Book } from "./Book" export function App() { const element = useRoutes([ { path: "/", element: <Home /> }, { path: "/books", children: [ { index: true, element: <BookList /> }, { path: ":id", element: <Book /> } ] } ]) return element }
import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { Book } from "./Book"

export function App() {
  const element = useRoutes([
    {
      path: "/",
      element: <Home />
    },
    {
      path: "/books",
      children: [
        { index: true, element: <BookList /> },
        { path: ":id", element: <Book /> }
      ]
    }
  ])

  return element
}

این دو کامپوننت هر دو دقیقاً مسیرهای مشابهی دارند و تنها تفاوت آن‌ها در نحوه تعریف آن‌ها است. اگر بخواهیم که از هوک

useRoutes
useRoutesاستفاده کنیم، تمام پراپ‌هایی که معمولاً به کامپوننت‌های
Route
Routeارسال می‌کنیم، فقط به‌عنوان جفت key/value ارسال می‌شوند.

مدیریت Navigation

اکنون که می‌دانیم چگونه مسیرهای خود را تعریف کنیم، باید در مورد نحوه جابه‌جایی بین آن‌ها هم صحبت کنیم. این بخش به سه قسمت تقسیم خواهد شد:

  1. Link Navigation
  2. Manual Navigation
  3. Navigation Data

Link Navigation

ابتدا می‌خواهیم درمورد link navigation صحبت کنیم زیرا ساده‌ترین و رایج‌ترین شکل navigation است که با آن روبه‌رو خواهیم شد. ما قبلاً درمورد ابتدایی‌ترین شکل link navigation با استفاده از کامپوننت

Link
Linkصحبت کرده‌ایم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Link to="/">Home</Link>
<Link to="/books">Books</Link>
<Link to="/">Home</Link> <Link to="/books">Books</Link>
<Link to="/">Home</Link>
<Link to="/books">Books</Link>

این کامپوننت‌های

Link
Link می‌توانند کمی پیچیده‌تر شوند. به عنوان مثال می‌توانیم لینک‌های absolute مانند لینک‌های بالا داشته باشیم یا اینکه می‌توانیم لینک‌هایی داشته باشیم که نسبت به کامپوننت فعلی، در حال رندر شدن هستند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Link to="/">Home</Link>
<Link to="../">Back</Link>
<Link to="edit">Edit</Link>
<Link to="/">Home</Link> <Link to="../">Back</Link> <Link to="edit">Edit</Link>
<Link to="/">Home</Link>
<Link to="../">Back</Link>
<Link to="edit">Edit</Link>

برای مثال تصور کنید در مسیر

/books/3
/books/3با لینک‌های بالا هستیم. لینک اول به مسیر
/
/منتهی می‌شود زیرا یک مسیر absolute است. هر مسیری که با یک
/
/ شروع می‌شود یک مسیر absolute به‌شمار می‌آید. لینک دوم به مسیر
/books
/booksمنتهی می‌شود زیرا یک لینک relative است که از
/books/3
/books/3یک سطح بالاتر رفته و به
/books
/booksمی‌رسد. در نهایت، لینک سوم به صفحه
/books/3/edit
/books/3/editمی‌رود، زیرا مسیری را در پراپ
to
toبه انتهای لینک فعلی اضافه می‌کند، زیرا یک لینک relative می‌باشد.

علاوه بر پراپ

to
to، سه پراپ دیگر وجود دارد که برای کامپوننت
Link
Linkمهم هستند.

replace

پراپ

replace
replaceیک مقدار بولین است که وقتی روی
true
trueتنظیم شود باعث می‌شود این لینک جایگزین صفحه فعلی در تاریخچه مرورگر شود. تصور کنید تاریخچه مرورگر زیر را داریم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/
/books
/books/3
/ /books /books/3
/
/books
/books/3

اگر روی لینکی کلیک کنیم که به صفحه

/books/3/edit
/books/3/editمی‌رود اما دارای ویژگی
replace
replaceمی‌باشد که روی
true
true تنظیم شده است، در این حالت تاریخچه جدید ما به این شکل خواهد بود:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/
/books
/books/3/edit
/ /books /books/3/edit
/
/books
/books/3/edit

صفحه‌ای که در حال حاضر در آن بودیم با صفحه جدید جایگزین شد. این به این معنی است که اگر روی دکمه بازگشت (Back) در صفحه جدید کلیک کنیم، به جای صفحه

/books/3
/books/3به صفحه
/books
/booksبرمی‌گردیم.

reloadDocument

این پراپ یک مقدار بولین دیگر است و  کار با آن بسیار ساده می‌باشد. اگر مقدار آن روی

true
true تنظیم شود، کامپوننت
Link
Link مانند یک تگ anchor معمولی عمل می‌کند و به جای اینکه فقط محتوای داخل کامپوننت
Routes
Routesخود را مجدداً رندر کند، یک صفحه کامل را رفرش می‌کند.

state

پراپ نهایی

state
stateنام دارد.
state
state به ما این امکان را می‌دهد تا داده‌هایی را به همراه
Link
Link خود ارسال کنیم که در هیچ کجای URL نمایش داده نمی‌شود. این همان چیزی است که وقتی درمورد navigation data صحبت می‌کنیم به طور عمیق‌تر بررسی خواهیم کرد.

NavLink

المنت بعدی که می‌خواهیم در مورد آن صحبت کنیم کامپوننت

NavLink
NavLinkاست. این کامپوننت دقیقاً مانند کامپوننت
Link
Link کار می‌کند، اما به طور خاص برای نشان دادن stateهای فعال روی لینک‌ها، به عنوان مثال در nav barها است. به طور پیش‌فرض اگر ویژگی
to
toیک
NavLink
NavLink با URL صفحه فعلی یکسان باشد لینک یک کلاس
active
activeبه آن اضافه می‌کند که می‌توانیم برای استایل‌سازی از آن استفاده کنیم. اگر این کافی نباشد، می‌توانیم تابعی با پارامتر
isActive
isActiveرا به
className
classNameیا
style
styleprops یا childهای
NavLink
NavLink ارسال کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<NavLink
to="/"
style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
>
Home
</NavLink>
<NavLink to="/" style={({ isActive }) => ({ color: isActive ? "red" : "black" })} > Home </NavLink>
<NavLink
  to="/"
  style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
>
  Home
</NavLink>

NavLink
NavLink همچنین دارای یک prop به نام
end
endاست که برای کمک به مسیریابی تودرتو مورد استفاده قرار می‌گیرد. به عنوان مثال، اگر در صفحه
/books/3
/books/3هستیم به این معنی است که در حال رندر کردن کامپوننت
Book
Bookای هستیم که در مسیر
/books
/booksقرار دارد. این بدان معناست که اگر یک
NavLink
NavLink با یک پراپ
to
toتنظیم شده روی
/books
/booksداشته باشیم، فعال در نظر گرفته می‌شود. این موضوع به این دلیل است که اگر URL با پراپ
to
to در
NavLink
NavLink مطابقت داشته باشد یا اگر
Route
Routeفعلی رندر شده در داخل یک کامپوننت parent باشد که دارای
path
pathای باشد که منطبق بر پراپ
NavLink
NavLink است، در این صورت یک
NavLink
NavLink فعال در نظر گرفته می‌شود. اگر این رفتار پیش‌فرض را نمی‌خواهیم، می‌توانیم پراپ
end
end را روی
true
trueتنظیم کنیم. این کار باعث می‌شود تا URL صفحه دقیقاً با پراپ
to
to در
NavLink
NavLink مطابقت داشته باشد.

Manual Navigation

گاهی اوقات می‌خواهیم براساس مواردی مانند ارسال فرم یا عدم دسترسی به یک صفحه خاص کاربر را به صورت دستی به یک مسیر خاصی هدایت کنیم. در چنین مواقعی باید از کامپوننت

Navigate
Navigateو یا از هوک
useNavigation
useNavigationاستفاده کنیم.

کامپوننت Navigate

کامپوننت

Navigate
Navigate یک کامپوننت واقعاً ساده است که وقتی رندر می‌شود کاربر را به طور خودکار به پراپ
to
to از کامپوننت
Navigate
Navigate هدایت می‌کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Navigate to="/" />
<Navigate to="/" />
<Navigate to="/" />

این کامپوننت تمام ویژگی‌های کامپوننت

Link
Linkرا به اشتراک می‌گذارد بنابراین به راحتی می‌توانیم آن را به
to
to،
replace
replaceو
state
stateارسال کنیم.

هوک useNavigation

استفاده از هوک

useNavigation
useNavigation بسیار ساده است زیرا هیچ پارامتری را دریافت نمی‌کند و یک تابع
navigate
navigateرا برمی‌گرداند که می‌توانیم از آن برای هدایت کاربر به صفحات خاص استفاده کنیم. این تابع
navigate
navigate دو پارامتر دارد. پارامتر اول لوکیشین
to
toاست که می‌خواهیم کاربر را به آن هدایت کنیم و پارامتر دوم یک آبجکت است که می‌تواند کلیدهایی برای
replace
replace و
state
state داشته باشد.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const navigate = useNavigate()
function onSubmit() {
// Submit form results
navigate("/books", { replace: true, state: { bookName: "Fake Title" }})
}
const navigate = useNavigate() function onSubmit() { // Submit form results navigate("/books", { replace: true, state: { bookName: "Fake Title" }}) }
const navigate = useNavigate()

function onSubmit() {
  // Submit form results
  navigate("/books", { replace: true, state: { bookName: "Fake Title" }})
}

کد بالا کاربر را به مسیر

/books
/booksهدایت می‌کند. همچنین جایگزین مسیر فعلی در تاریخچه مرورگر خواهد شد و برخی از اطلاعات state را نیز منتقل خواهد کرد.

راه دیگری که می‌توانیم از تابع

navigate
navigateاستفاده کنیم ارسال یک عدد به آن است. این کار به ما این امکان را می‌دهد تا کلیک کردن روی باتن forward/back را شبیه سازی کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
navigate(-1) // Go back one page in history
navigate(-3) // Go back three pages in history
navigate(1) // Go forward one page in history
navigate(-1) // Go back one page in history navigate(-3) // Go back three pages in history navigate(1) // Go forward one page in history
navigate(-1) // Go back one page in history
navigate(-3) // Go back three pages in history
navigate(1) // Go forward one page in history

Navigation Data

درنهایت وقت آن است که در مورد انتقال داده‌ها بین صفحات صحبت کنیم. ۳ روش اصلی برای انتقال داده بین صفحات وجود دارد که عبارتند از:

  1. پارامترهای پویا
  2. پارامترهای جستجو
  3. داده‌های State/Location

پارامترهای پویا

قبلاً در مورد نحوه استفاده از پارامترهای پویا در URLها با استفاده از هوک

useParams
useParamsصحبت کرده‌ایم. این بهترین راه برای مدیریت اطلاعات ارسالی مانند idها به‌شمار می‌آید.

پارامترهای جستجو

پارامترهای جستجو همه پارامترهایی هستند که بعد از

?
?در یک URL قرار می‌گیرند، مثلا  (
?name=Kyle&age=27
?name=Kyle&age=27). برای کار با پارامترهای جستجو، باید از هوک
useSearchParams
useSearchParamsاستفاده کنیم که بسیار شبیه به هوک
useState
useStateعمل می‌کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useSearchParams } from "react-router-dom"
export function SearchExample() {
const [searchParams, setSearchParams] = useSearchParams({ n: 3 })
const number = searchParams.get("n")
return (
<>
<h1>{number}</h1>
<input
type="number"
value={number}
onChange={e => setSearchParams({ n: e.target.value })}
/>
</>
)
}
import { useSearchParams } from "react-router-dom" export function SearchExample() { const [searchParams, setSearchParams] = useSearchParams({ n: 3 }) const number = searchParams.get("n") return ( <> <h1>{number}</h1> <input type="number" value={number} onChange={e => setSearchParams({ n: e.target.value })} /> </> ) }
import { useSearchParams } from "react-router-dom"

export function SearchExample() {
  const [searchParams, setSearchParams] = useSearchParams({ n: 3 })
  const number = searchParams.get("n")

  return (
    <>
      <h1>{number}</h1>
      <input
        type="number"
        value={number}
        onChange={e => setSearchParams({ n: e.target.value })}
      />
    </>
  )
}

در این مثال، یک مقدار input داریم که با تایپ کردن، بخش جستجوی URL ما به‌روزرسانی می‌شود. به عنوان مثال، اگر ورودی ما دارای مقدار ۳۲ باشد URL ما شبیه

http://localhost:3000?n=32
http://localhost:3000?n=32خواهد بود. هوک
useSearchParams
useSearchParamsهمانند هوک
useState
useStateیک مقدار اولیه می‌گیرد که در مورد مثال ما این مقدار اولیه
n
nروی ۳ تنظیم شده است. این هوک سپس دو مقدار را برمی‌گرداند. مقدار اول تمام پارامترهای جستجوی ما است و مقدار دوم تابعی برای به‌روزرسانی پارامترهای جستجوی می‌باشد. تابع set فقط یک آرگومان می‌گیرد که مقدار جدید پارامترهای جستجوی ما است. اولین مقداری که شامل پارامترهای جستجو می‌باشد کمی گیج‌کننده به نظر می‌رسد زیرا این مقدار از نوع
URLSearchParams
URLSearchParamsمی‌باشد. به همین دلیل است که در خط ۵ مثال بالا باید از دستور
.get
.getاستفاده کنیم.

داده‌های State/Location

آخرین نوع داده‌ای که می‌توانیم آن را ذخیره کنیم داده‌های state و location است. همه این اطلاعات از طریق هوک

useLocation
useLocationقابل دسترسی هستند. استفاده از این هوک بسیار ساده است زیرا فقط یک مقدار برمی‌گرداند و هیچ پارامتری را نمی‌گیرد.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const location = useLocation()
const location = useLocation()
const location = useLocation()

اگر آدرس

http://localhost/books?n=32#id
http://localhost/books?n=32#idرا داشته باشیم، مقدار بازگشتی هوک
useLocation
useLocationبه این شکل خواهد بود:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
pathname: "/books",
search: "?n=32",
hash: "#id",
key: "2JH3G3S",
state: null
}
{ pathname: "/books", search: "?n=32", hash: "#id", key: "2JH3G3S", state: null }
{
  pathname: "/books",
  search: "?n=32",
  hash: "#id",
  key: "2JH3G3S",
  state: null
}

این آبجکت location شامل تمام اطلاعات مربوط به URL ما می‌باشد. همچنین حاوی یک کلید منحصربه‌فرد است که اگر بخواهیم اطلاعات را برای زمانی که کاربر روی دکمه Back کلیک می‌کند تا به صفحه بازگردد ذخیره کنیم، می‌توانیم آن را برای ذخیره‌سازی کش مورد استفاده قرار دهیم. همینطور یک ویژگی State داریم که از هوک

useLocation
useLocation بازگردانده شده است. این داده state می‌تواند هر چیزی باشد و بدون ذخیره شدن در URL بین صفحات ارسال می‌شود. برای مثال اگر روی
Link
Linkکلیک کنیم که به شکل زیر است:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Link to="/books" state={{ name: "Kyle" }}>
<Link to="/books" state={{ name: "Kyle" }}>
<Link to="/books" state={{ name: "Kyle" }}>

در این صورت مقدار state در آبجکت location روی

{ name: "Kyle" }
{ name: "Kyle" }تنظیم می‌شود.

به عنوان مثال اگر بخواهیم بین صفحاتی که نباید در URL ذخیره شوند پیام‌های ساده‌ای ارسال کنیم استفاده از این روش می‌تواند بسیار مفید باشد. یک مثال خوب برای این موضوع می‌تواند چیزی شبیه پیام موفقیت‌آمیز بودن ایجاد کتاب جدید باشد.

Routers In Depth

تاکنون در این مقاله حدود ۹۵٪ از آنچه که باید در مورد React Router بدانیم را باهم بررسی کردیم، اما این کتابخانه هنوز مفاهیمی دارد که باید به آن‌ها نیز بپردازیم. در بخش مفاهیم اولیه در مورد تعریف router صحبت کرده و به

BrowserRouter
BrowserRouterو
NativeRouter
NativeRouterاشاره کردیم، اما این دو تنها روترهای موجود نیستند. در واقع ۶ روتر وجود دارد که در این بخش هر یک از آن‌ها را به طور کامل بررسی خواهیم کرد.

BrowserRouter

این روتر پیش‌فرضی است که اگر روی یک وب‌ اپلیکیشن کار می‌کنیم باید از آن استفاده کنیم.

BrowserRouter
BrowserRouter روتری است که در ۹۹٪ از همه برنامه‌هایی که داریم مورد استفاده قرار خواهد گرفت، زیرا همه موارد استفاده از مسیریابی معمولی را پوشش می‌دهد. هر یک از روترهای دیگری که در مورد آن‌ها صحبت خواهیم کرد موارد استفاده مخصوص خود را دارند، بنابراین اگر با این موارد استفاده مناسب برنامه ما نباشند،
BrowserRouter
BrowserRouter همان چیزی است که باید آن را انتخاب کنیم.

NativeRouter

NativeRouter
NativeRouter در اصل معادل
BrowserRouter
BrowserRouter است اما برای React Native اختصاص دارد. اگر از React Native استفاده می‌کنیم باید این روتر را به کار بگیریم.

HashRouter

این روتر بسیار شبیه به

BrowserRouter
BrowserRouter کار می‌کند، اما تفاوت اصلی این است که به جای اینکه URL را به چیزی مانند
http://localhost:3000/books
http://localhost:3000/booksتغییر دهد، آن را مانند
http://localhost:3000/#/books
http://localhost:3000/#/booksدر هش ذخیره می‌کند. همانطور که می‌بینیم این URL دارای یک
#
#بعد از URL است که نشان‌دهنده بخش هش URL می‌باشد. هر چیزی در این بخش فقط اطلاعات اضافی است که معمولاً یک id در صفحه را برای اهداف مربوط به اسکرول کردن نشان می‌دهد. زیرا یک صفحه به طور خودکار به المنت با id نشان داده شده توسط هش هنگام لود شدن صفحه اسکرول می‌شود.

در React Router این هش در واقع برای ذخیره اطلاعات id برای اسکرول استفاده نمی‌شود، بلکه اطلاعات مربوط به URL فعلی را ذخیره می‌کند. دلیل اینکه React Router این کار را انجام می‌دهد این است که برخی از ارائه‌دهندگان هاست به ما اجازه نمی‌دهند تا URL صفحه خود را تغییر دهیم. ما در این شرایط بسیار نادر می‌خواهیم از

HashRouter
HashRouterاستفاده کنیم زیرا این روتر URL واقعی صفحه ما را تغییر نمی‌دهد و فقط هش صفحه را عوض می‌کند. اگر می‌توانید از هر URLای استفاده کنیم نباید این روتر را در برنامه‌های خود به کار بگیریم.

HistoryRouter

HistoryRouter
HistoryRouter(که در حال حاضر
unstable_HistoryRouter
unstable_HistoryRouterنامیده می‌شود) روتری است که به ما این امکان را می‌دهد که به صورت دستی آبجکت history‌ای را که React Router برای ذخیره تمام اطلاعات مربوط به تاریخچه مسیریابی برنامه استفاده می‌کند، کنترل کنیم. این آبجکت history کمک می‌کند تا مطمئن شویم مواردی مانند باتن back و forward در مرورگر به درستی کار می‌کنند.

این روتری است که احتمالاً هرگز نباید از آن استفاده کنیم، مگر اینکه دلیل بسیار خاصی داشته باشیم که بخواهیم رفتار تاریخچه پیش‌فرض React Router را بازنویسی یا کنترل کنیم.

MemoryRouter

MemoryRouter
MemoryRouterکمی متفاوت از بقیه روترهایی است که در مورد آن‌ها صحبت کردیم، زیرا به جای ذخیره اطلاعات درمورد مسیر فعلی در URL مرورگر، اطلاعات مسیریابی را مستقیماً در حافظه ذخیره می‌کند. بدیهی است که استفاده از این روتر برای عملیات مسیریابی معمولی انتخاب درستی نیست اما استفاده از آن برای زمانی که در حال نوشتن تست‌هایی برای برنامه خود هستیم که به مرورگر دسترسی ندارند، می‌تواند بسیار مفید باشد.

به دلیل نحوه عملکرد React Router ما باید کامپوننت‌های خود را داخل روتر قرار دهیم، در غیر این صورت تمام کد مسیریابی ما دچار خطا می‌شود. به این معنی که حتی اگر می‌خواهیم یک کامپوننت را تست کنیم، باید آن را داخل یک روتر قرار دهیم وگرنه خطاهایی ایجاد می‌کند. اگر کد خود را به گونه‌ای تست می‌کنیم که به مرورگر دسترسی نداشته باشد (مانند unit testing)، روترهایی که تاکنون در مورد آن‌ها صحبت کرده‌ایم دچار خطا می‌شوند زیرا همه آن‌ها برای URL به مرورگر وابسته هستند. از طرف دیگر

MemoryRouter
MemoryRouter تمام اطلاعات خود را در حافظه ذخیره می‌کند به این معنی که هرگز به مرورگر دسترسی پیدا نمی‌کند. درنتیجه زمانی که برای تست کردن کامپوننت‌های خود از unit testing استفاده می‌کنیم این روتر یک انتخاب ایده‌آل به شمار می‌آید. به غیر از این مورد خاص،
MemoryRouter
MemoryRouter هرگز استفاده نمی‌شود.

StaticRouter

روتر نهایی

StaticRouter
StaticRouterاست. این روتر به‌طور خاص برای سروری مورد استفاده قرار می‌گیرد که برنامه‌های React ما را رندر می‌کند، زیرا یک پراپ
location
locationرا می‌گیرد و برنامه ما را با استفاده از آن
location
location به عنوان URL نمایش می‌دهد. این روتر در واقع نمی‌تواند هیچ مسیریابی انجام دهد و فقط یک صفحه استاتیک را ارائه می‌دهد، اما برای رندر سرور یک انتخاب عالی محسوب می‌شود زیرا ما می‌خواهیم که فقط HTML برنامه خود را روی سرور رندر کنیم و سپس کلاینت می‌تواند تمام مسیریابی‌های ما و موارد دیگر را تنظیم کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<StaticRouter location="/books">
<App />
</StaticRouter>
<StaticRouter location="/books"> <App /> </StaticRouter>
<StaticRouter location="/books">
  <App />
</StaticRouter>

جمع‌بندی

React Router یک کتابخانه بسیار بزرگ با هزاران ویژگی شگفت‌انگیز است و به همین دلیل است که اکثر افراد از این کتابخانه برای مسیریابی در برنامه‌های خود استفاده می‌کنند.

دیدگاه‌ها:

افزودن دیدگاه جدید