660 words
3 minutes
Scroll Problem in React Router

ازاي تتحكم في الـscroll لما تنتقل بين الصفحات في React Router 🚀#

المشكلة 🤔#

لو بتستخدم React Router، أكيد قابلتك المشكلة دي قبل كده: لما بتنتقل من صفحة لصفحة، الـscroll مش بيرجع لفوق تلقائي. يعني لو كنت في نص الصفحة وضغطت على لينك يوديك لصفحة تانية، الصفحة الجديدة هتفتح في نفس المكان - مش من فوق.

ليه المشكلة دي بتحصل؟ 🧐#

علشان نفهم المشكلة كويس، لازم نعرف شوية حاجات عن React Router والـSingle Page Applications (SPA)

  1. React Router بيتعامل مع التطبيق كـSPA، يعني صفحة واحدة بتتغير محتواها
  2. مفيش refresh للصفحة لما بننتقل بين الـroutes
  3. React Router مش بيغير الـscroll position تلقائي، علشان
    • ممكن تكون عايز ترجع لنفس المكان لما تدخل على الصفحة تاني
    • ده السلوك الافتراضي في الـSPA

الحلول المتاحة 💡#

الحل الأول: استخدام ScrollRestoration من React Router 📜#

ده أبسط حل وأسرع طريقة. كل اللي عليك تعمله إنك تضيف الـcomponent ده في الـroot component بتاع التطبيق

import { ScrollRestoration } from 'react-router-dom';

function Root() {
  return (
    <>
      <nav>
        {/* قائمة التنقل بتاعتك */}
      </nav>
      
      <Outlet />
      <ScrollRestoration />
    </>
  );
}

الحل التاني: استخدام useLayoutEffect و scrollTo 🔄#

الحل ده مناسب لو عايز:

  • تتحكم أكتر في سلوك الـscroll
  • تمنع أي انيميشن للـscroll
  • تضمن إن الـscroll يحصل قبل ما المستخدم يشوف الصفحة الجديدة
import { useLayoutEffect } from 'react';

function Page() {
  useLayoutEffect(() => {
    window.scrollTo({ top: 0, behavior: 'instant' });
  }, []);

  return (
    <div>
      {/* محتوى الصفحة */}
    </div>
  );
}

ليه useLayoutEffect مش useEffect؟ 🤓#

في فرق مهم بين الاتنين:

  • useEffect بيتنفذ بعد ما React يخلص rendering
  • useLayoutEffect بيتنفذ قبل ما المستخدم يشوف أي تغيير على الشاشة

ده بيمنع حاجة اسمها FOUC (Flash of Unscrolled Content)، يعني المستخدم مش هيشوف الصفحة في المكان القديم قبل ما تتنقل لفوق.

مثال كامل للتطبيق 🌟#

import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';
import { ScrollRestoration } from 'react-router-dom';

// Root Component
function Root() {
  return (
    <>
      <nav>
        <Link to="/">الرئيسية</Link>
        <Link to="/another">صفحة تانية</Link>
      </nav>
      
      <Outlet />
      <ScrollRestoration />
    </>
  );
}

// تعريف الـRoutes
const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    children: [
      {
        path: '/',
        element: <SomePage />
      },
      {
        path: '/another',
        element: <AnotherPage />
      }
    ]
  }
]);

// التطبيق الرئيسي
function App() {
  return <RouterProvider router={router} />;
}

نصائح إضافية 💪#

  1. تأكد إن الـScrollRestoration موجود في أعلى مستوى في التطبيق يعني إيـه؟

أين تضع الـScrollRestoration في تطبيقك؟ 🎯#

المكان الصح ✅#

// App.jsx أو root.jsx
import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';
import { ScrollRestoration } from 'react-router-dom';

function RootLayout() {
  return (
    <div>
      <header>
        <nav>{/* القائمة بتاعتك */}</nav>
      </header>
      
      <ScrollRestoration /> {/* هنا المكان الصح */}
      <Outlet />
      <footer>{/* الفوتر بتاعك */}</footer>
    </div>
  );
}

const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    children: [
      { path: '/', element: <HomePage /> },
      { path: '/about', element: <AboutPage /> }
    ]
  }
]);

export default function App() {
  return <RouterProvider router={router} />;
}

أماكن غلط ❌#

// صفحة فرعية - HomePage.jsx
function HomePage() {
  return (
    <div>
      <ScrollRestoration /> {/* ❌ غلط - مش هيشتغل كويس في كل الحالات */}
      <h1>الصفحة الرئيسية</h1>
    </div>
  );
}

// مكون فرعي - Sidebar.jsx
function Sidebar() {
  return (
    <aside>
      <ScrollRestoration /> {/* ❌ غلط - بعيد عن الـrouting logic */}
      <nav>{/* محتوى السايدبار */}</nav>
    </aside>
  );
}

ليه المكان مهم؟ 🤔#

  1. الـScope:

    • ScrollRestoration لازم يكون في مستوى أعلى من كل الـroutes
    • علشان يقدر يتحكم في الـscroll مع كل تغيير في الـURL
  2. التأثير:

    • لما يكون في المستوى الأعلى، هيأثر على كل الصفحات
    • هيتأكد إن الـscroll بيرجع لفوق مع كل navigation
  3. الـPerformance:

    • وجوده في مكان واحد أفضل من تكراره في كل صفحة
    • بيمنع المشاكل اللي ممكن تحصل من وجود أكتر من instance

أفضل الممارسات 📝#

  1. حط الـScrollRestoration في الـroot layout component
  2. خليه قريب من الـOutlet component
  3. تأكد إنه موجود مرة واحدة بس في التطبيق
  4. متحطوش في components فرعية أو صفحات منفصلة

مثال لهيكل ملفات مثالي 📁#

src/
├── main.jsx          # entry point
├── App.jsx           # فيه RouterProvider
├── layouts/
│   └── RootLayout.jsx  # ✅ حط ScrollRestoration هنا
└── pages/
    ├── HomePage.jsx    # ❌ مش هنا
    └── AboutPage.jsx   # ❌ ولا هنا
  1. لو محتاج تخصص سلوك الـscroll، استخدم الحل التاني
  2. اختبر التطبيق على مختلف المتصفحات علشان تتأكد إن كل حاجة شغالة تمام

الخلاصة ✨#

  • استخدم ScrollRestoration للحل السريع والبسيط
  • استخدم useLayoutEffect لو محتاج تحكم أكتر
  • فكر في تجربة المستخدم واختار الحل المناسب لتطبيقك

Join our whatsapp group here
My Channel here