useRouter
إذا كنت ترغب في الوصول إلى كائن router
داخل أي مكون وظيفي في تطبيقك، يمكنك استخدام خطاف useRouter
. إليك المثال التالي:
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.asPath === href ? 'red' : 'black',
}
const handleClick = (e) => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
export default ActiveLink
useRouter
هو خطاف React، مما يعني أنه لا يمكن استخدامه مع الفئات. يمكنك إما استخدام withRouter أو تغليف فئتك داخل مكون وظيفي.
كائن router
فيما يلي تعريف كائن router
الذي يتم إرجاعه بواسطة كل من useRouter
و withRouter
:
pathname
:String
- مسار ملف المسار الحالي الذي يأتي بعد/pages
. لذلك، لا يتم تضمينbasePath
أوlocale
أو الشرطة المائلة الزائدة (trailingSlash: true
).query
:Object
- سلسلة الاستعلام المحللة إلى كائن، بما في ذلك معلمات المسار الديناميكي. سيكون كائنًا فارغًا أثناء التصيير المسبق إذا لم تستخدم الصفحة التصيير من جانب الخادم (SSR). القيمة الافتراضية هي{}
.asPath
:String
- المسار كما يظهر في متصفح المستخدم بما في ذلك معلمات البحث واحترام إعداداتtrailingSlash
. لا يتم تضمينbasePath
وlocale
.isFallback
:boolean
- ما إذا كانت الصفحة الحالية في وضع الاحتياطي (fallback mode).basePath
:String
- basePath النشط (إذا تم تمكينه).locale
:String
- اللغة النشطة (إذا تم تمكينها).locales
:String[]
- جميع اللغات المدعومة (إذا تم تمكينها).defaultLocale
:String
- اللغة الافتراضية الحالية (إذا تم تمكينها).domainLocales
:Array<{domain, defaultLocale, locales}>
- أي لغات نطاق مهيأة.isReady
:boolean
- ما إذا كانت حقول الموجه محدثة من جانب العميل وجاهزة للاستخدام. يجب استخدامها فقط داخل طرقuseEffect
وليس للتصيير الشرطي على الخادم. راجع الوثائق ذات الصلة لحالة الاستخدام مع الصفحات المحسنة تلقائيًا بشكل ثابت.isPreview
:boolean
- ما إذا كان التطبيق حاليًا في وضع المعاينة (preview mode).
استخدام حقل
asPath
قد يؤدي إلى عدم تطابق بين العميل والخادم إذا تم تصيير الصفحة باستخدام التصيير من جانب الخادم أو التحسين الثابت التلقائي. تجنب استخدامasPath
حتى يصبح حقلisReady
true
.
تتضمن الطرق التالية داخل router
:
router.push
يتحكم في الانتقالات من جانب العميل، هذه الطريقة مفيدة للحالات التي لا يكون فيها next/link
كافيًا.
router.push(url, as, options)
url
:UrlObject | String
- عنوان URL للانتقال إليه (راجع وثائق وحدة URL في Node.JS لخصائصUrlObject
).as
:UrlObject | String
- مزخرف اختياري للمسار الذي سيظهر في شريط عنوان URL للمتصفح. قبل Next.js 9.5.3 كان هذا يستخدم للمسارات الديناميكية.options
- كائن اختياري مع خيارات التهيئة التالية:scroll
- قيمة منطقية اختيارية، تتحكم في التمرير إلى أعلى الصفحة بعد التنقل. القيمة الافتراضية هيtrue
.shallow
: تحديث مسار الصفحة الحالية دون إعادة تشغيلgetStaticProps
أوgetServerSideProps
أوgetInitialProps
. القيمة الافتراضية هيfalse
.locale
- سلسلة اختيارية، تشير إلى لغة الصفحة الجديدة.
لا تحتاج إلى استخدام
router.push
لعناوين URL الخارجية. window.location أكثر ملاءمة لتلك الحالات.
الانتقال إلى pages/about.js
، وهو مسار محدد مسبقًا:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/about')}>
اضغط علي
</button>
)
}
الانتقال إلى pages/post/[pid].js
، وهو مسار ديناميكي:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/post/abc')}>
اضغط علي
</button>
)
}
تحويل المستخدم إلى pages/login.js
، مفيد للصفحات خلف المصادقة:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// هنا يمكنك جلب وإرجاع المستخدم
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
if (!(user || loading)) {
router.push('/login')
}
}, [user, loading])
return <p>جاري التحويل...</p>
}
إعادة تعيين الحالة بعد التنقل
عند الانتقال إلى نفس الصفحة في Next.js، لن يتم إعادة تعيين حالة الصفحة افتراضيًا لأن React لا يفك التحميل إلا إذا تغير المكون الأصلي.
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
const [count, setCount] = useState(0)
return (
<div>
<h1>الصفحة: {router.query.slug}</h1>
<p>العدد: {count}</p>
<button onClick={() => setCount(count + 1)}>زيادة العدد</button>
<Link href="/one">واحد</Link> <Link href="/two">اثنان</Link>
</div>
)
}
في المثال أعلاه، الانتقال بين /one
و /two
لن يعيد تعيين العدد. يتم الحفاظ على useState
بين عمليات التصيير لأن مكون React الرئيسي، Page
، هو نفسه.
إذا كنت لا تريد هذا السلوك، لديك خياران:
-
تحديث كل حالة يدويًا باستخدام
useEffect
. في المثال أعلاه، يمكن أن يبدو كالتالي:useEffect(() => { setCount(0) }, [router.query.slug])
-
استخدم
key
في React لإخبار React بإعادة تحميل المكون. للقيام بذلك لجميع الصفحات، يمكنك استخدام تطبيق مخصص:pages/_app.js import { useRouter } from 'next/router' export default function MyApp({ Component, pageProps }) { const router = useRouter() return <Component key={router.asPath} {...pageProps} /> }
مع كائن URL
يمكنك استخدام كائن URL بنفس الطريقة التي يمكنك استخدامها مع next/link
. يعمل لكل من معلمتي url
و as
:
import { useRouter } from 'next/router'
export default function ReadMore({ post }) {
const router = useRouter()
return (
<button
type="button"
onClick={() => {
router.push({
pathname: '/post/[pid]',
query: { pid: post.id },
})
}}
>
اضغط هنا لقراءة المزيد
</button>
)
}
router.replace
مشابه لخاصية replace
في next/link
، router.replace
سيمنع إضافة إدخال URL جديد إلى سجل history
.
router.replace(url, as, options)
- واجهة برمجة التطبيقات (API) لـ
router.replace
هي نفسها تمامًا لواجهة برمجة التطبيقات لـrouter.push
.
انظر إلى المثال التالي:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
اضغط علي
</button>
)
}
router.prefetch
جلب الصفحات مسبقًا لتحقيق انتقالات أسرع من جانب العميل. هذه الطريقة مفيدة فقط للتنقلات بدون next/link
، حيث يتولى next/link
جلب الصفحات مسبقًا تلقائيًا.
هذه ميزة للإنتاج فقط. لا يقوم Next.js بجلب الصفحات مسبقًا في وضع التطوير.
router.prefetch(url, as, options)
url
- عنوان URL المطلوب جلبها مسبقًا، بما في ذلك المسارات الصريحة (مثل/dashboard
) والمسارات الديناميكية (مثل/product/[id]
).as
- مزخرف اختياري لـurl
. قبل Next.js 9.5.3 كان هذا يستخدم لجلب المسارات الديناميكية مسبقًا.options
- كائن اختياري مع الحقول المسموح بها التالية:locale
- يسمح بتوفير لغة مختلفة عن اللغة النشطة. إذا كانتfalse
، يجب أن يتضمنurl
اللغة حيث لن يتم استخدام اللغة النشطة.
لنفترض أن لديك صفحة تسجيل دخول، وبعد تسجيل الدخول، تقوم بتحويل المستخدم إلى لوحة التحكم. لتلك الحالة، يمكننا جلب لوحة التحكم مسبقًا لتحقيق انتقال أسرع، كما في المثال التالي:
import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Login() {
const router = useRouter()
const handleSubmit = useCallback((e) => {
e.preventDefault()
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
/* بيانات النموذج */
}),
}).then((res) => {
// قم بالانتقال السريع من جانب العميل إلى صفحة لوحة التحكم التي تم جلبها مسبقًا
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// جلب صفحة لوحة التحكم مسبقًا
router.prefetch('/dashboard')
}, [router])
return (
<form onSubmit={handleSubmit}>
{/* حقول النموذج */}
<button type="submit">تسجيل الدخول</button>
</form>
)
}
router.beforePopState
في بعض الحالات (على سبيل المثال، إذا كنت تستخدم خادم مخصص)، قد ترغب في الاستماع إلى popstate والقيام بشيء ما قبل أن يتصرف الموجه بناءً عليه.
router.beforePopState(cb)
cb
- الوظيفة التي سيتم تشغيلها على أحداثpopstate
الواردة. تتلقى الوظيفة حالة الحدث ككائن مع الخصائص التالية:url
:String
- المسار للحالة الجديدة. هذا عادةً اسمpage
.as
:String
- عنوان URL الذي سيظهر في المتصفح.options
:Object
- خيارات إضافية مرسلة بواسطة router.push.
إذا أعادت cb
false
، فلن يتعامل موجه Next.js مع popstate
، وسيكون مسؤوليتك التعامل معه في تلك الحالة. راجع تعطيل توجيه نظام الملفات.
يمكنك استخدام beforePopState
لمعالجة الطلب، أو فرض تحديث SSR، كما في المثال التالي:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// أريد فقط السماح بهذين المسارين!
if (as !== '/' && as !== '/other') {
// اجعل SSR يصيير المسارات السيئة كـ 404.
window.location.href = as
return false
}
return true
})
}, [router])
return <p>مرحبًا بك في الصفحة</p>
}
router.back
الانتقال للخلف في السجل. يعادل النقر على زر الرجوع في المتصفح. ينفذ window.history.back()
.
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.back()}>
اضغط هنا للعودة
</button>
)
}
router.reload
إعادة تحميل عنوان URL الحالي. يعادل النقر على زر التحديث في المتصفح. ينفذ window.location.reload()
.
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.reload()}>
اضغط هنا لإعادة التحميل
</button>
)
}
router.events
يمكنك الاستماع إلى أحداث مختلفة تحدث داخل موجه Next.js. إليك قائمة بالأحداث المدعومة:
routeChangeStart(url, { shallow })
- يتم تشغيله عندما يبدأ المسار في التغيير.routeChangeComplete(url, { shallow })
- يتم تشغيله عندما يتغير المسار بالكامل.routeChangeError(err, url, { shallow })
- يتم تشغيله عند حدوث خطأ أثناء تغيير المسارات، أو إلغاء تحميل المسار.err.cancelled
- يشير إلى ما إذا تم إلغاء التنقل.
beforeHistoryChange(url, { shallow })
- يتم تشغيله قبل تغيير سجل المتصفح.hashChangeStart(url, { shallow })
- يتم تشغيله عندما يتغير الهاش ولكن ليس الصفحة.hashChangeComplete(url, { shallow })
- يتم تشغيله عندما يتغير الهاش ولكن ليس الصفحة.
ملاحظة مهمة: هنا
url
هو عنوان URL الذي يظهر في المتصفح، بما في ذلكbasePath
.
على سبيل المثال، للاستماع إلى حدث الموجه routeChangeStart
، افتح أو أنشئ pages/_app.js
واشترك في الحدث، كما يلي:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url, { shallow }) => {
console.log(
`التطبيق يتغير إلى ${url} ${
shallow ? 'مع' : 'بدون'
} التوجيه السطحي`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// إذا تم فك تحميل المكون، قم بإلغاء الاشتراك
// من الحدث باستخدام طريقة `off`:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
نستخدم تطبيقًا مخصصًا (
pages/_app.js
) لهذا المثال للاشتراك في الحدث لأنه لا يتم فك تحميله عند تنقلات الصفحة، ولكن يمكنك الاشتراك في أحداث الموجه في أي مكون في تطبيقك.
يجب تسجيل أحداث الموجه عندما يتم تحميل مكون (useEffect أو componentDidMount / componentWillUnmount) أو بشكل إلزامي عند حدوث حدث.
إذا تم إلغاء تحميل مسار (على سبيل المثال، بالنقر على رابطين بسرعة متتالية)، سيتم تشغيل routeChangeError
. وسيحتوي err
الممرر على خاصية cancelled
مضبوطة على true
، كما في المثال التالي:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChangeError = (err, url) => {
if (err.cancelled) {
console.log(`تم إلغاء المسار إلى ${url}!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// إذا تم فك تحميل المكون، قم بإلغاء الاشتراك
// من الحدث باستخدام طريقة `off`:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}
أخطاء ESLint المحتملة
بعض الطرق المتاحة على كائن router
تُرجع Promise. إذا كان لديك قاعدة ESLint no-floating-promises مفعّلة، يُنظر بتعطيلها إما بشكل عام، أو للأسطر المتأثرة.
إذا كانت تطبيقك يحتاج هذه القاعدة، يجب إما استخدام void
مع الـ Promise - أو استخدام دالة async
، انتظار الـ Promise باستخدام await
، ثم استخدام void
مع استدعاء الدالة. هذا لا ينطبق عندما يتم استدعاء الطريقة من داخل معالج حدث onClick
.
الطرق المتأثرة هي:
router.push
router.replace
router.prefetch
حلول محتملة
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// هنا يمكنك جلب وإعادة المستخدم
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// تعطيل التحقق من ESLint للسطر التالي - هذا هو الحل الأنظف
// eslint-disable-next-line no-floating-promises
router.push('/login')
// استخدام void مع الـ Promise المُعاد من router.push
if (!(user || loading)) {
void router.push('/login')
}
// أو استخدام دالة async، انتظار الـ Promise باستخدام await، ثم استخدام void مع استدعاء الدالة
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>جاري التوجيه...</p>
}
withRouter
إذا لم يكن useRouter
مناسبًا لك، يمكن لـ withRouter
أيضًا إضافة نفس كائن router
إلى أي مكون.
طريقة الاستخدام
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
TypeScript
لاستخدام مكونات الفئة مع withRouter
، يجب أن يقبل المكون خاصية router:
import React from 'react'
import { withRouter, NextRouter } from 'next/router'
interface WithRouterProps {
router: NextRouter
}
interface MyComponentProps extends WithRouterProps {}
class MyComponent extends React.Component<MyComponentProps> {
render() {
return <p>{this.props.router.pathname}</p>
}
}
export default withRouter(MyComponent)