useRouter
إذا كنت ترغب في الوصول إلى كائن الموجه داخل أي مكون وظيفي في تطبيقك، يمكنك استخدام خطاف 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])
-
استخدم مفتاح React
key
لإخبار 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)