React: React Router: موضوعات متقدمة

آخر تحديث: 2026-08-26

نجح توم في تنفيذ التوجيه الأساسي، لكن المتطلبات الجديدة لا تزال تظهر: يجب إعادة توجيه المستخدمين تلقائيًا إلى لوحة التحكم بعد تسجيل الدخول؛ وتتطلب بعض الصفحات تسجيل الدخول للوصول إليها؛ ومع نمو التطبيق، يستغرق تحميل الصفحة الرئيسية وقتًا أطول؛ كما يجب أن تدعم صفحة قائمة المنتجات معلمات التصفية في عنوان URL... وهو بحاجة إلى تعلم الميزات المتقدمة لـ React Router للتعامل مع هذه السيناريوهات الواقعية.


1. ما ستتعلمه



2. المخططات المفاهيمية

100%
flowchart TD
    U[User Actions] --> A{Are you logged in??}
    A -->|Not logged in| B[ProtectedRoute<br/>→ Redirect to /login]
    A -->|Logged in| C{Permissions?}
    C -->|No permission| D[→ 403 Page]
    C -->|Has permission| E[Load the target page]
    E --> F{Is the component lazily loaded??}
    F -->|is | G[Suspense<br/>Show loading]
    G --> H[Rendering Page]
    F -->|No| H
    style A fill:#fff3e0,stroke:#f57c00
    style B fill:#ffcdd2,stroke:#d32f2f
    style G fill:#e1f5fe,stroke:#0288d1
    style H fill:#e8f5e9,stroke:#388e3c

طلب المستخدم → يقوم «Route Guard» بالتحقق من تسجيل الدخول/الأذونات → يتم تحميل الصفحة بعد الموافقة (يظهر «جاري التحميل» أثناء التحميل المؤجل) → العرض النهائي.



3. سيناريو واقعي

يتطلب تطبيق توم ما يلي: بعد تسجيل الدخول بنجاح، يجب إعادة توجيه المستخدم تلقائيًا إلى لوحة التحكم؛ ويجب أن تكون صفحة لوحة التحكم محمية بتسجيل الدخول؛ ويجب ألا يُسمح بالوصول إلى لوحة الإدارة إلا للمستخدمين الذين يحملون دور «admin»؛ ويجب أن تُحمَّل الصفحة الرئيسية بسرعة عند الظهور لأول مرة (مع تحميل الصفحات الكبيرة عند الطلب)؛ ويجب أن تتضمن عناوين URL لصفحة قائمة المنتجات معلمات التصفية وتقسيم الصفحات لتسهيل المشاركة.

(1) التنقل البرمجي: useNavigate

يحتاج توم إلى معالجة عمليات إرسال النماذج في صفحة تسجيل الدخول، وعند إتمام الإرسال بنجاح، إعادة توجيه المستخدمين إلى صفحات مختلفة بناءً على أدوارهم. هذا النوع من التنقل، الذي يتم تشغيله بواسطة منطق البرمجة، لا يمكنه استخدام <Link> (نظرًا لأن الروابط لا يمكن تشغيلها إلا بنقرات المستخدمين)؛ وبدلاً من ذلك، يجب استخدام useNavigate.

useNavigate تُرجع دالة navigate تدعم ثلاث طرق للاستدعاء:

الاستخدام التأثير مثال
navigate('/path') الانتقال إلى مسار محدد (إضافة إدخال جديد إلى مكدس السجل) navigate('/dashboard')
navigate('/path', { replace: true }) استبدال السجل الحالي (لا يمكن العودة إلى هذه النقطة) إعادة التوجيه بعد تسجيل الدخول
navigate(-1) رجوع العودة إلى الصفحة السابقة

▶ المثال 1: التنقل البرمجي بعد تسجيل الدخول

JSX
import { useNavigate, useLocation } from 'react-router-dom'

function LoginPage() {
  const navigate = useNavigate()
  const location = useLocation()

  // from  URL Retrieve the URL to redirect to after login from the query parameters
  const from = location.state?.from?.pathname || '/dashboard'

  function handleLogin(event) {
    event.preventDefault()
    const formData = new FormData(event.target)
    const username = formData.get('username')

    // Simulate a login request
    fakeLogin(username).then(user => {
      if (user.role === 'admin') {
        navigate('/admin', { replace: true })     // Replace Record,Cannot go back to the login page
      } else {
        navigate(from, { replace: true })          // Redirect to the page you were trying to access before logging in
      }
    })
  }

  // Login Form
  return (
    <form onSubmit={handleLogin}>
      <input name="username" placeholder="Username" required />
      <button type="submit">Log In</button>
      <button type="button" onClick={() => navigate(-1)}>Back</button>
    </form>
  )
}

// Simulated Login API
async function fakeLogin(username) {
  await new Promise(r => setTimeout(r, 500))
  return { name: username, role: username === 'admin' ? 'admin' : 'user' }
}
▶ جرّب الكود

دور useLocation: location.state يمكنه استقبال بيانات الحالة التي يتم تمريرها من Link أو navigate. على سبيل المثال، عند إعادة التوجيه إلى صفحة تسجيل الدخول داخل ProtectedRoute، يمكنك تخزين مسار الصفحة التي كان المستخدم يحاول الوصول إليها في الأصل في state، بحيث يتم إعادة توجيه المستخدم تلقائيًا إلى تلك الصفحة بعد تسجيل الدخول.

(2) حارس المسار: ProtectedRoute

نوع الحارس منطق الفحص السلوك في حالة الفشل السيناريوهات النموذجية
Login Guard isAuthenticated إعادة التوجيه إلى /login لوحة التحكم، حسابي
حارس الأذونات user.role === 'admin' إعادة التوجيه إلى /403 لوحة الإدارة
ميزة «Switch Guard» featureFlags.xEnabled إعادة التوجيه إلى /upgrade الميزات المدفوعة
الحارس الشرطي profileComplete إعادة التوجيه إلى /onboarding دليل تسجيل الدخول لأول مرة

تتطلب صفحة لوحة التحكم الخاصة بـ«توم» تسجيل الدخول للوصول إليها، كما تتطلب لوحة الإدارة دور «admin». وهو بحاجة إلى آلية «حراسة» — آلية تتحقق من حالة المستخدم قبل عرض الصفحة وتقوم بإعادة توجيهه إذا لم تتوفر الشروط المطلوبة.

في جوهره، يُعد RouteGuard مكونًا مغلفًا: فهو يستقبل المكونات الفرعية وينفذ منطق التحقق من الصحة قبل عرضها. إذا نجح التحقق من الصحة، فإنه يعرض المكونات الفرعية؛ وإلا، فإنه يعيد التوجيه إلى صفحة أخرى باستخدام <Navigate>.

▶ المثال 2: حاجز التوجيه متعدد الطبقات

JSX
import { Navigate, useLocation } from 'react-router-dom'

// Simulated Certification Hook
function useAuth() {
  return {
    user: { name: 'Tom', role: 'admin' },  // In actual projects, starting from Context or  store Get
    isAuthenticated: true
  }
}

// First Floor:Login Guard
function ProtectedRoute({ children }) {
  const { isAuthenticated } = useAuth()
  const location = useLocation()

  if (!isAuthenticated) {
    // Save the user's destination to state in ,Redirect back after logging in
    return <Navigate to="/login" state={{ from: location }} replace />
  }

  return children
}

// Second Floor:Character Guard
function AdminRoute({ children }) {
  const { user } = useAuth()

  if (user.role !== 'admin') {
    return <Navigate to="/403" replace />
  }

  return children
}

// Used in router configuration
function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/login" element={<LoginPage />} />
      <Route path="/403" element={<AccessDenied />} />

      {/* You must log in */}
      <Route path="/dashboard" element={
        <ProtectedRoute>
          <Dashboard />
        </ProtectedRoute>
      } />

      {/* You must log in + admin Character */}
      <Route path="/admin" element={
        <ProtectedRoute>
          <AdminRoute>
            <AdminPanel />
          </AdminRoute>
        </ProtectedRoute>
      } />
    </Routes>
  )
}

نمط الحماية المتداخلة: يقوم ProtectedRoute الخارجي بالتحقق من «ما إذا كان المستخدم قد سجل الدخول»، بينما يتحقق AdminRoute الداخلي من «ما إذا كان المستخدم يتمتع بصلاحيات إدارية». وهذا يؤدي إلى فصل المسؤوليات ويتيح إمكانية إعادة الاستخدام. وإذا دعت الحاجة إلى دور «التحرير» في المستقبل، فما عليك سوى إضافة EditorRoute.

(3) التحميل المؤجل: React.lazy + Suspense

مع نمو التطبيق، لاحظ توم أن تحميل الصفحة الرئيسية أصبح يستغرق وقتًا أطول فأطول — نظرًا لأن جميع أكواد كل الصفحات كانت مجمعة في حزمة واحدة، كان على المستخدمين تنزيل كود لوحة الإدارة كلما زاروا الصفحة الرئيسية. تتيح ميزة React.lazy تقسيم كود المكونات إلى أجزاء منفصلة لا يتم تحميلها إلا عند الحاجة.

JSX
import { lazy, Suspense } from 'react'

// These components will not be bundled into the main bundle in 
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
const AdminPanel = lazy(() => import('./pages/AdminPanel'))
const UserList = lazy(() => import('./pages/UserList'))
▶ جرّب الكود

▶ المثال 3: التحميل المؤجل على مستوى المسار

JSX
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'

// Lazy-load all page components
const Home = lazy(() => import('./pages/Home'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
const ProductList = lazy(() => import('./pages/ProductList'))
const ProductDetail = lazy(() => import('./pages/ProductDetail'))
const Settings = lazy(() => import('./pages/Settings'))
const NotFound = lazy(() => import('./pages/NotFound'))

// Loading component
function PageLoader() {
  return (
    <div style={{
      display: 'flex', justifyContent: 'center', alignItems: 'center',
      height: '100vh', fontSize: '1.2rem', color: '#666'
    }}>
      <div className="spinner" />
      <span style={{ marginLeft: '12px' }}>Page is loading...</span>
    </div>
  )
}

function App() {
  return (
    <BrowserRouter>
      {/* Suspense Wrap all lazy-loading routes */}
      <Suspense fallback={<PageLoader />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/products" element={<ProductList />} />
          <Route path="/products/:id" element={<ProductDetail />} />
          <Route path="/settings" element={<Settings />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  )
}

export default App
▶ جرّب الكود

كيفية عمل التحميل المتأخر: لا يقوم React بتحميل الجزء /dashboard ديناميكيًا إلا عندما يزور المستخدم /dashboard للمرة الأولى. وأثناء التحميل، يتم عرض المكون fallback من Suspense. وبمجرد اكتمال التحميل، يتم استبداله بمكون Dashboard الفعلي.

مزايا الأداء: بافتراض أن الحزمة الأصلية تبلغ 500 كيلوبايت، فإن التحميل المؤجل يقلل حجم الحزمة الرئيسية إلى 100 كيلوبايت، بحيث يبلغ حجم كل صفحة حوالي 80 كيلوبايت. وعندما يزور المستخدمون الصفحة الرئيسية، لا يحتاجون سوى إلى تنزيل 100 كيلوبايت بدلاً من 500 كيلوبايت — مما يؤدي إلى تحسن يبلغ حوالي 5 أضعاف في سرعة التحميل في الجزء المرئي من الصفحة.



4. useSearchParams: إدارة معلمات استعلام عناوين URL

طريقة معلمات URL واجهة برمجة التطبيقات (API) مثال على الصيغة البيانات القابلة للتطبيق
معلمة المسار useParams() /products/:id{ id: '42' } المعرّف المطلوب (معرّف المورد)
معلمات الاستعلام useSearchParams() ?sort=price&page=2 التصفية/الترتيب/التقسيم إلى صفحات (اختياري)
هاش useLocation().hash #section-3 روابط المرساة للصفحة
الحالة useLocation().state navigate('/path', { state }) تمرير البيانات المخفية عبر الصفحات

يجب أن تدعم صفحة قائمة المنتجات الخاصة بـ«توم» ميزات مثل التصفية حسب الفئة، والترتيب حسب السعر، وتقسيم الصفحات، ويجب أن تنعكس معايير التصفية هذه في عنوان URL — حتى يتمكن المستخدمون من مشاركة الروابط التي تم تصفية نتائجها مع الآخرين.

(1) قراءة وكتابة معلمات الاستعلام

useSearchParams تُرجع كائنًا شبيهًا بـ Map يتيح لك قراءة وكتابة معلمات استعلام URL بنفس الطريقة التي تعمل بها Map. وعلى غرار useState، فإنها تُرجع قيمة ووظيفة تعيين.

JSX
import { useSearchParams } from 'react-router-dom'

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams()

  const category = searchParams.get('category') || 'All'
  const sort = searchParams.get('sort') || 'default'
  const page = Number(searchParams.get('page')) || 1

  function updateFilter(key, value) {
    setSearchParams(prev => {
      if (value) prev.set(key, value)
      else prev.delete(key)
      return prev
    })
  }

  return (
    <div>
      <p>Current Category:{category} | Sort:{sort} | Page: {page}</p>
      <button onClick={() => updateFilter('category', 'Electronics')}>Electronics Categories</button>
      <button onClick={() => updateFilter('sort', 'price')}>Sort by Price</button>
      <button onClick={() => updateFilter('page', String(page + 1))}>Next Page</button>
      <button onClick={() => setSearchParams({})}>Clear Filters</button>
    </div>
  )
}
// URL It will be updated in real time:/products?category=Electronics&sort=price&page=2
▶ جرّب الكود

▶ المثال 4: وظيفة تصفية المنتجات الكاملة

JSX
import { useSearchParams } from 'react-router-dom'

// Simulated Product Data
const allProducts = [
  { id: 1, name: 'iPhone 16', category: 'Electronics', price: 6999 },
  { id: 2, name: 'React Programming Books', category: 'Books', price: 79 },
  { id: 3, name: 'Mechanical Keyboard', category: 'Electronics', price: 399 },
  { id: 4, name: 'Introduction to Design Patterns', category: 'Books', price: 59 },
  { id: 5, name: 'Bluetooth Headphones', category: 'Electronics', price: 899 },
]

function ProductListPage() {
  const [searchParams, setSearchParams] = useSearchParams()

  // from  URL Read Filter Criteria
  const category = searchParams.get('category') || ''
  const sortBy = searchParams.get('sort') || 'name'
  const page = parseInt(searchParams.get('page') || '1', 10)
  const pageSize = 3

  // Filtering
  let filtered = category
    ? allProducts.filter(p => p.category === category)
    : allProducts

  // Sort
  if (sortBy === 'price') {
    filtered = [...filtered].sort((a, b) => a.price - b.price)
  } else {
    filtered = [...filtered].sort((a, b) => a.name.localeCompare(b.name))
  }

  // Pagination
  const totalPages = Math.ceil(filtered.length / pageSize)
  const paged = filtered.slice((page - 1) * pageSize, page * pageSize)

  // Update Filter Criteria
  function setFilter(key, value) {
    setSearchParams(prev => {
      const next = new URLSearchParams(prev)
      if (value) {
        next.set(key, value)
      } else {
        next.delete(key)
      }
      next.set('page', '1')  // Return to the first page when switching filters
      return next
    })
  }

  return (
    <div>
      <div style={{ marginBottom: '16px' }}>
        <label>Categories:
          <select value={category} onChange={e => setFilter('category', e.target.value)}>
            <option value="">All</option>
            <option value="Electronics">Electronics</option>
            <option value="Books">Books</option>
          </select>
        </label>
        <label style={{ marginLeft: '16px' }}>Sort:
          <select value={sortBy} onChange={e => setFilter('sort', e.target.value)}>
            <option value="name">Name</option>
            <option value="price">Price</option>
          </select>
        </label>
      </div>

      <ul>
        {paged.map(p => (
          <li key={p.id}>{p.name} — ${p.price}({p.category})</li>
        ))}
      </ul>

      <div>
        {Array.from({ length: totalPages }, (_, i) => (
          <button
            key={i}
            onClick={() => setSearchParams(prev => {
              const next = new URLSearchParams(prev)
              next.set('page', String(i + 1))
              return next
            })}
            style={{ fontWeight: page === i + 1 ? 'bold' : 'normal' }}
          >
            {i + 1}
          </button>
        ))}
      </div>

      <p>Currently URL:/products?category={category}&sort={sortBy}&page={page}</p>
    </div>
  )
}

الكلمة المفتاحية: تمثل معلمات الاستعلام في عنوان URL «حالة قابلة للمشاركة». فعندما يقوم المستخدم بتطبيق عوامل التصفية ونسخ عنوان URL لإرساله إلى زميل له، سيشاهد هذا الزميل بالضبط نفس النتائج التي تمت تصفيتها عند فتح الرابط. وهذا الأمر غير ممكن عند استخدام useState لإدارة شروط التصفية.



5. الاستراتيجيات التنظيمية لتكوين مسارات التوجيه

عندما يرتفع عدد مسارات المشروع إلى عدة عشرات، فإن كتابة جميع المسارات في مكون واحد يجعل صيانة الكود أمراً صعباً. لذا، يتعين على توم فصل تكوين المسارات إلى وحدة منفصلة.

(1) ملف تكوين التوجيه

JSX
// src/routes/index.js
import { lazy } from 'react'

// Centrally manage all routing definitions
const routes = [
  {
    path: '/',
    component: lazy(() => import('../pages/Home')),
    exact: true
  },
  {
    path: '/login',
    component: lazy(() => import('../pages/Login')),
  },
  {
    path: '/dashboard',
    component: lazy(() => import('../pages/Dashboard')),
    protected: true  // You must log in
  },
  {
    path: '/admin',
    component: lazy(() => import('../pages/AdminPanel')),
    protected: true,
    adminOnly: true   // Required admin Permissions
  },
  {
    path: '/products',
    component: lazy(() => import('../pages/ProductList')),
  },
  {
    path: '/products/:id',
    component: lazy(() => import('../pages/ProductDetail')),
  },
  {
    path: '*',
    component: lazy(() => import('../pages/NotFound')),
  }
]

export default routes
▶ جرّب الكود

▶ المثال 5: مُصوِّر المسار

JSX
// src/routes/AppRouter.jsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { Suspense } from 'react'
import routes from './index'
import ProtectedRoute from '../components/ProtectedRoute'
import AdminRoute from '../components/AdminRoute'

function renderRoutes(routeList) {
  return routeList.map(route => {
    const Component = route.component

    let element = <Component />

    // On-Demand Package Guard
    if (route.protected) {
      element = <ProtectedRoute>{element}</ProtectedRoute>
    }
    if (route.adminOnly) {
      element = <AdminRoute>{element}</AdminRoute>
    }

    return (
      <Route key={route.path} path={route.path} element={element} />
    )
  })
}

function AppRouter() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading......</div>}>
        <Routes>
          {renderRoutes(routes)}
        </Routes>
      </Suspense>
    </BrowserRouter>
  )
}

export default AppRouter
▶ جرّب الكود

وتتمثل ميزة هذا النهج في أن إعدادات التوجيه تتمركز في مكان واحد، ويتم تغليف منطق الحماية تلقائيًا، ولا تتطلب إضافة صفحة جديدة سوى إضافة كائن إلى routes/index.js.

(2) useLocation: مراقبة تغييرات المسار

بالإضافة إلى التنقل، يحتاج «توم» أيضًا إلى مراقبة التغييرات في المسار في حالات معينة — على سبيل المثال، لإرسال بيانات التتبع عند تغير مسار الصفحة، أو إغلاق النوافذ المنبثقة، أو إعادة تعيين حالات النماذج. يمكن لـ useLocation استرداد معلومات عنوان URL الحالي، وعند استخدامه بالاقتران مع useEffect، يمكنه مراقبة التغييرات في المسار.

JSX
import { useLocation } from 'react-router-dom'
import { useEffect } from 'react'

function PageTracker() {
  const location = useLocation()

  useEffect(() => {
    // Triggered whenever the path changes
    console.log('Page Views:', location.pathname + location.search)

    // Tracking Event Reporting
    analytics.pageView({
      path: location.pathname,
      search: location.search,
      timestamp: Date.now()
    })
  }, [location])  // Dependency location Object
  // Note:location.pathname or  location.search Any change will trigger

  return null  // This component does not render anything UI
}

// in  App Used in
function App() {
  return (
    <BrowserRouter>
      <PageTracker />  {/* Place Routes External,Always Listen */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </BrowserRouter>
  )
}
▶ جرّب الكود

الخصائص الرئيسية التي تُرجعها useLocation: pathname (المسار، مثل /products/42search (سلسلة الاستعلام، مثل ?category=electronicshash (تجزئة عنوان URL، مثل #section-2state (بيانات الحالة التي يتم تمريرها عبر Link أو navigate).



❓ أسئلة شائعة

س كيف أختار بين useNavigate وLink؟
ج يُستخدم Link للتنقل الذي يتم تشغيله بنقرات المستخدم (مثل الروابط في شريط التنقل، أو مسار التنقل، أو قوائم المقالات). يُستخدم useNavigate للتنقل الذي يتم تشغيله بواسطة منطق البرمجة (مثل عمليات إعادة التوجيه بعد تسجيل الدخول، أو بعد إرسال النموذج، أو عمليات إعادة التوجيه المجدولة، أو التنقل للخلف/للأمام). القاعدة الأساسية: تفاعل المستخدم → Link؛ منطق البرمجة → useNavigate.
س هل يمكن تداخل حراس المسار على مستويات متعددة؟ هل هناك أي احتياطات يجب أخذها في الاعتبار؟
ج نعم. من الأنماط الشائعة أن يقوم الحارس الخارجي ProtectedRoute بالتحقق من «ما إذا كان المستخدم قد سجل الدخول»، بينما يتحقق الحارس الداخلي AdminRoute من «ما إذا كان المستخدم يتمتع بأذونات إدارية». عند تداخل الحراس، تأكد من أن كل حارس يتولى مسؤولية واحدة؛ لا تتحقق من حالة تسجيل الدخول وأذونات الدور معًا ضمن حارس واحد. إذا كان عدد الحراس كبيرًا جدًّا، ففكر في استخدام مصفوفة تكوين مقترنة بحلقة لتغليف طبقات متعددة من الحراس تلقائيًّا.
س ما هي قيود التحميل المؤجل في React.lazy؟
ج هناك ثلاث قيود: (1) لا يمكن استخدامه إلا مع المكونات التي يتم تصديرها باستخدام التصدير الافتراضي (export default)؛ (2) يجب استخدامه داخل مكون Suspense؛ (3) لا يدعم العرض من جانب الخادم (بالنسبة لـ SSR، استخدم @loadable/component بدلاً من ذلك). بالإضافة إلى ذلك، إذا كانت سرعة الشبكة بطيئة أثناء تحميل مكون يتم تحميله بشكل مؤجل، فسيشاهد المستخدمون المحتوى البديل؛ ويوصى بتصميم المحتوى البديل بحيث يكون صغير الحجم وسريعًا.
س ما الفرق بين استخدام useSearchParams وuseState لإدارة شروط التصفية؟
ج يقوم useSearchParams بمزامنة الحالة مع عنوان URL — حيث يمكن للمستخدمين مشاركة الروابط وإضافة الصفحات إلى الإشارات المرجعية والتنقل للأمام وللخلف. أما الحالة التي يديرها useState فتوجد فقط في الذاكرة وتُفقد عند تحديث الصفحة. ومع ذلك، فإن useSearchParams يتسبب في عبء إضافي على الأداء (تؤدي تغييرات عنوان URL إلى إعادة عرض المكون). بالنسبة للسيناريوهات التي تتضمن تحديثات متكررة (مثل سحب شريط التمرير)، يُنصح باستخدام useState أولاً ثم مزامنة الحالة مع عنوان URL بمجرد تأكيدها.
س ما الفرق بين حراس المسار (route guards) والبرمجيات الوسيطة (middleware)؟
ج حراس المسار هي أدوات اعتراض على مستوى مكونات React — فهي تُلف حول المكون المستهدف، وبعد التحقق من الشروط، تقرر ما إذا كان سيتم عرض المكون أم إعادة التوجيه. أما البرمجيات الوسيطة فهي أدوات اعتراض على مستوى جانب الخادم في Next.js — فهي تنفذ المنطق (مثل المصادقة، وإعادة التوجيه، واختبار A/B) بعد وصول الطلب إلى الخادم ولكن قبل عرض الصفحة. تعمل حراس React Router على جانب العميل، بينما تعمل البرامج الوسيطة في Next.js على جانب الخادم في بيئة Edge Runtime.

📖 ملخص


📝 تمارين

  1. تنفيذ صفحة تسجيل الدخول: بعد أن يقوم المستخدم بإدخال اسم المستخدم الخاص به، استخدم useNavigate للانتقال إلى لوحة التحكم، واستخدم replace: true أثناء عملية الانتقال لمنع المستخدم من العودة إلى صفحة تسجيل الدخول.
  2. استخدم ProtectedRoute لحماية مسارات لوحة التحكم، بحيث يتم إعادة التوجيه إلى صفحة تسجيل الدخول إذا لم يكن المستخدم مسجلاً للدخول، ثم إعادة التوجيه تلقائيًا إلى الصفحة التي كان المستخدم ينوي زيارتها في الأصل بعد نجاح عملية تسجيل الدخول.
  3. استخدم useSearchParams لتنفيذ صفحة قائمة المنتجات: يجب أن تدعم هذه الصفحة التصفية حسب الفئة، والفرز حسب السعر، وتقسيم الصفحات. يجب أن تنعكس معايير التصفية في عنوان URL، كما يجب الحفاظ على معايير التصفية بعد تحديث الصفحة.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%