React
React Router — التنقل بين الصفحات في React 2026
📅 2026-10-17⏱ 9 دقائق قراءة
في المقالات السابقة، بنينا تطبيقات React **بصفحة واحدة**. لكن التطبيقات الحقيقية تحتاج **صفحات متعددة**. هنا يأتي دور **React Router**.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لتعلم React Router وكيفية بناء تطبيق متعدد الصفحات، مع تمارين وحلول.
## ما هو React Router؟
**React Router** هي **مكتبة** تُضيف التنقل (Routing) لتطبيقات React. تسمح لك بـ:
- **تبديل الصفحات** بدون إعادة تحميل.
- **تمرير معاملات** في الرابط.
- **حماية المسارات** (Auth).
- **التنقل المتداخل** (Nested Routes).
**تشبيه بسيط:** تطبيق React **الواحد** يمكن أن يحتوي على **عدة صفحات**. React Router هو **الموجّه** الذي يعرض الصفحة الصحيحة بناءً على الرابط.
## لماذا نحتاج React Router؟
**بدون React Router:**
- ❌ إما صفحة واحدة فقط.
- ❌ أو إعادة تحميل الصفحة لكل تنقل.
**مع React Router:**
- ✅ صفحات متعددة.
- ✅ تنقل فوري (SPA).
- ✅ روابط قابلة للمشاركة.
- ✅ زر الرجوع يعمل.
## أنواع المكتبات
هناك **مكتبتان** رئيسيتان:
| المكتبة | الوصف | الاستخدام |
|---------|-------|-----------|
| **React Router** | المكتبة الكلاسيكية | للـ SPA |
| **TanStack Router** | مكتبة حديثة | Type-safe |
**سنستخدم React Router** لأنها الأكثر شعبية.
## تثبيت React Router
في Terminal، اذهب إلى مشروعك، واكتب:
```bash
npm install react-router-dom
```
**النتيجة:** ستُضاف المكتبة إلى `package.json`.
## الإعداد الأساسي
### 1. في `src/main.tsx`
```tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
```
**شرح:** `BrowserRouter` يُغلّف التطبيق كله ليُتيح التنقل.
### 2. في `src/App.tsx`
```tsx
import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
export default App;
```
**شرح:**
- **`Routes`:** حاوية لكل المسارات.
- **`Route`:** مسار واحد (path + element).
- **`path="*"`:** مسار افتراضي (404).
### 3. إنشاء الصفحات
**أنشئ `src/pages/Home.tsx`:**
```tsx
function Home() {
return (
<div>
<h1>🏠 الصفحة الرئيسية</h1>
<p>مرحباً بك في موقعنا!</p>
</div>
);
}
export default Home;
```
**أنشئ `src/pages/About.tsx`:**
```tsx
function About() {
return (
<div>
<h1>ℹ️ من نحن</h1>
<p>نحن فريق عربي متخصص في التقنية.</p>
</div>
);
}
export default About;
```
**أنشئ `src/pages/NotFound.tsx`:**
```tsx
import { Link } from "react-router-dom";
function NotFound() {
return (
<div style={{ textAlign: "center", padding: 40 }}>
<h1>404</h1>
<p>الصفحة غير موجودة</p>
<Link to="/">العودة للرئيسية</Link>
</div>
);
}
export default NotFound;
```
## التنقل بين الصفحات
### 1. باستخدام `<Link>`
```tsx
import { Link } from "react-router-dom";
function Navbar() {
return (
<nav>
<Link to="/">الرئيسية</Link>
<Link to="/about">من نحن</Link>
<Link to="/contact">اتصل بنا</Link>
</nav>
);
}
```
**الفرق عن `<a>`:**
- `<a>`: يعيد تحميل الصفحة.
- `<Link>`: تنقل فوري (SPA).
### 2. باستخدام `useNavigate`
```tsx
import { useNavigate } from "react-router-dom";
function LoginButton() {
const navigate = useNavigate();
const handleClick = () => {
// ... منطق تسجيل الدخول
navigate("/dashboard");
};
return <button onClick={handleClick}>تسجيل الدخول</button>;
}
```
### 3. باستخدام `NavLink` (للروابط النشطة)
```tsx
import { NavLink } from "react-router-dom";
function Navbar() {
return (
<nav>
<NavLink to="/" className={({ isActive }) => isActive ? "active" : ""}>
الرئيسية
</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>
من نحن
</NavLink>
</nav>
);
}
```
**الفائدة:** `NavLink` يعرف متى يكون الرابط **نشطاً** (الصفحة الحالية).
## المسارات الديناميكية
### 1. تعريف المسار
```tsx
<Route path="/user/:id" element={<UserProfile />} />
```
**`:id`** هو **معامل ديناميكي**.
### 2. قراءة المعامل
```tsx
import { useParams } from "react-router-dom";
function UserProfile() {
const { id } = useParams<{ id: string }>();
return (
<div>
<h1>الملف الشخصي للمستخدم #{id}</h1>
</div>
);
}
```
**النتيجة:**
- `/user/1` → "الملف الشخصي للمستخدم #1"
- `/user/42` → "الملف الشخصي للمستخدم #42"
## المسارات المتداخلة (Nested Routes)
```tsx
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
</Route>
</Routes>
```
**`Layout.tsx`:**
```tsx
import { Outlet } from "react-router-dom";
import Navbar from "./Navbar";
function Layout() {
return (
<div>
<Navbar />
<main>
<Outlet /> {/* ← الصفحة الحالية تُعرض هنا */}
</main>
<footer>© 2026</footer>
</div>
);
}
```
**الفائدة:** Navbar و Footer يظهران في **كل الصفحات**.
## حماية المسارات (Protected Routes)
```tsx
import { Navigate } from "react-router-dom";
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isLoggedIn = localStorage.getItem("token");
if (!isLoggedIn) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}
// الاستخدام:
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
```
## مثال عملي: مدونة كاملة
### 1. الصفحات
**`src/pages/Home.tsx`:**
```tsx
import { Link } from "react-router-dom";
function Home() {
return (
<div>
<h1>📝 مدونتي</h1>
<Link to="/posts">تصفح المقالات</Link>
</div>
);
}
export default Home;
```
**`src/pages/Posts.tsx`:**
```tsx
import { Link } from "react-router-dom";
const posts = [
{ id: 1, title: "تعلم React", excerpt: "..." },
{ id: 2, title: "تعلم TypeScript", excerpt: "..." },
{ id: 3, title: "بناء API", excerpt: "..." },
];
function Posts() {
return (
<div>
<h1>📚 المقالات</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
</div>
);
}
export default Posts;
```
**`src/pages/PostDetail.tsx`:**
```tsx
import { useParams, Link } from "react-router-dom";
const posts = {
"1": { title: "تعلم React", content: "React مكتبة رائعة..." },
"2": { title: "تعلم TypeScript", content: "TypeScript تضيف أنواع..." },
"3": { title: "بناء API", content: "Node.js مع Express..." },
};
function PostDetail() {
const { id } = useParams<{ id: string }>();
const post = posts[id as keyof typeof posts];
if (!post) {
return (
<div>
<p>المقال غير موجود</p>
<Link to="/posts">العودة</Link>
</div>
);
}
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
<Link to="/posts">← العودة للمقالات</Link>
</article>
);
}
export default PostDetail;
```
### 2. `App.tsx`
```tsx
import { Routes, Route } from "react-router-dom";
import Layout from "./components/Layout";
import Home from "./pages/Home";
import Posts from "./pages/Posts";
import PostDetail from "./pages/PostDetail";
import NotFound from "./pages/NotFound";
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="posts" element={<Posts />} />
<Route path="posts/:id" element={<PostDetail />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
}
export default App;
```
## الاستعلامات (Query Parameters)
**الرابط:**
```
/posts?sort=date&filter=recent
```
**القراءة:**
```tsx
import { useSearchParams } from "react-router-dom";
function Posts() {
const [searchParams, setSearchParams] = useSearchParams();
const sort = searchParams.get("sort");
const filter = searchParams.get("filter");
return (
<div>
<p>الترتيب: {sort}</p>
<p>التصفية: {filter}</p>
<button onClick={() => setSearchParams({ sort: "title" })}>
ترتيب حسب العنوان
</button>
</div>
);
}
```
## تمارين عملية
### تمرين 1: موقع بسيط
أنشئ موقعاً بصفحتين: Home و About.
**الحل:**
```tsx
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
```
### تمرين 2: Navbar مع Link
أنشئ Navbar بروابط للصفحات.
**الحل:**
```tsx
function Navbar() {
return (
<nav>
<Link to="/">الرئيسية</Link>
<Link to="/about">من نحن</Link>
<Link to="/contact">اتصل بنا</Link>
</nav>
);
}
```
### تمرين 3: مسار ديناميكي
أنشئ صفحة `/user/:id` تعرض رقم المستخدم.
**الحل:**
```tsx
function User() {
const { id } = useParams();
return <h1>المستخدم #{id}</h1>;
}
```
### تمرين 4: 404
أنشئ صفحة 404 مع رابط للعودة.
**الحل:**
```tsx
function NotFound() {
return (
<div>
<h1>404</h1>
<Link to="/">العودة للرئيسية</Link>
</div>
);
}
```
### تمرين 5: التنقل برمجياً
أنشئ زر ينقلك لصفحة أخرى.
**الحل:**
```tsx
function GoButton() {
const navigate = useNavigate();
return <button onClick={() => navigate("/about")}>اذهب لمن نحن</button>;
}
```
### تمرين 6: NavLink نشط
أضف تنسيقاً للرابط النشط.
**الحل:**
```tsx
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>
من نحن
</NavLink>
```
### تمرين 7: مسارات متداخلة
أنشئ Layout مع Navbar و Footer.
**الحل:**
```tsx
function Layout() {
return (
<>
<Navbar />
<main><Outlet /></main>
<footer>© 2026</footer>
</>
);
}
```
### تمرين 8: حماية مسار
أنشئ مسار `/dashboard` محمي.
**الحل:**
```tsx
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isLoggedIn = !!localStorage.getItem("token");
return isLoggedIn ? <>{children}</> : <Navigate to="/login" />;
}
```
## حل المشاكل الشائعة
### 🔴 المشكلة 1: `useNavigate() may be used only in the context of a <Router>`
**السبب:** لم تُغلّف التطبيق بـ `BrowserRouter`.
**الحل:** أضف `<BrowserRouter>` في `main.tsx`.
### 🔴 المشكلة 2: الصفحة تُحدّث عند التنقل
**السبب:** استخدام `<a href>` بدلاً من `<Link>`.
**الحل:** استخدم `<Link to="...">`.
### 🔴 المشكلة 3: `useParams` لا يعمل
**السبب:** المسار غير ديناميكي.
**الحل:** تأكد من تعريف `:id` في المسار.
### 🔴 المشكلة 4: 404 في الإنتاج
**السبب:** السيرفر لا يعرف كيف يتعامل مع المسارات.
**الحل (Firebase):** أضف `rewrites` في `firebase.json`:
```json
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
```
**⚠️ مهم جداً:** بدون هذا، المسارات المباشرة (مثل `/about`) لن تعمل في الإنتاج!
## جدول مكونات React Router
<table>
<thead>
<tr>
<th>المكوّن/الدالة</th>
<th>الوظيفة</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BrowserRouter</code></td>
<td>يُغلّف التطبيق</td>
</tr>
<tr>
<td><code>Routes</code></td>
<td>حاوية المسارات</td>
</tr>
<tr>
<td><code>Route</code></td>
<td>مسار واحد</td>
</tr>
<tr>
<td><code>Link</code></td>
<td>رابط تنقل</td>
</tr>
<tr>
<td><code>NavLink</code></td>
<td>رابط مع حالة نشطة</td>
</tr>
<tr>
<td><code>Outlet</code></td>
<td>مكان عرض الصفحات الفرعية</td>
</tr>
<tr>
<td><code>Navigate</code></td>
<td>إعادة توجيه</td>
</tr>
<tr>
<td><code>useNavigate</code></td>
<td>دالة التنقل</td>
</tr>
<tr>
<td><code>useParams</code></td>
<td>قراءة معاملات المسار</td>
</tr>
<tr>
<td><code>useSearchParams</code></td>
<td>قراءة query params</td>
</tr>
</tbody>
</table>
## قائمة تحقق نهائية
<table>
<thead>
<tr>
<th>المهمة</th>
<th>الحالة</th>
</tr>
</thead>
<tbody>
<tr>
<td>تثبيت React Router</td>
<td>⬜</td>
</tr>
<tr>
<td>إعداد BrowserRouter</td>
<td>⬜</td>
</tr>
<tr>
<td>إنشاء Routes و Route</td>
<td>⬜</td>
</tr>
<tr>
<td>استخدام Link و NavLink</td>
<td>⬜</td>
</tr>
<tr>
<td>المسارات الديناميكية</td>
<td>⬜</td>
</tr>
<tr>
<td>المسارات المتداخلة</td>
<td>⬜</td>
</tr>
<tr>
<td>حماية المسارات</td>
<td>⬜</td>
</tr>
<tr>
<td>حل التمارين الثمانية</td>
<td>⬜</td>
</tr>
</tbody>
</table>
## ⚠️ مهم جداً: إعداد Firebase
لتعمل المسارات في الإنتاج على Firebase، **تأكد** من أن `firebase.json` يحتوي على:
```json
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
```
**بدون هذا، ستظهر صفحة 404 عند زيارة أي مسار مباشر.**
## ماذا بعد هذا المقال؟
الآن بعد أن أتقنت React Router، أنت جاهز للمقال التالي:
1. **Context API** — إدارة الحالة العامة.
2. **Redux Toolkit** — إدارة حالة متقدمة.
3. **مشروع متكامل** — تطبيق كامل.
## الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو React Router ولماذا نحتاجه.
- ✅ تثبيت وإعداد React Router.
- ✅ Routes و Route.
- ✅ Link, NavLink, useNavigate.
- ✅ المسارات الديناميكية (useParams).
- ✅ المسارات المتداخلة (Outlet).
- ✅ حماية المسارات (Protected Routes).
- ✅ حل 8 تمارين عملية.
**تذكر:** React Router يجعل تطبيقك **SPA حقيقي**. أتقنه جيداً.
**هل أتقنت React Router؟ شاركنا في التعليقات!**