Next.js เปลี่ยนเส้นทางจาก / ไปยังหน้าอื่น


15

ฉันใหม่ในNext.jsและฉันสงสัยว่าจะเปลี่ยนเส้นทางจากหน้าเริ่มต้น ( / ) ไปยัง/ hello-nextjsได้อย่างไร เมื่อผู้ใช้โหลดหน้าและหลังจากนั้นกำหนดว่าเส้นทาง === / redirect ไปยัง/ hello-nextjs

ในการตอบสนองเราเตอร์เราทำสิ่งที่ชอบ:

<Switch>
  <Route path="/hello-nextjs" exact component={HelloNextjs} />
  <Redirect to="/hello-nextjs" /> // or <Route path="/" exact render={() => <Redirect to="/hello-nextjs" />} />
</Switch>

1
เมื่อใดที่คุณต้องการให้การเปลี่ยนเส้นทางเกิดขึ้น
นิโก้

@ NicolòCozzaniเมื่อผู้ใช้โหลดหน้า และหลังจากนั้นให้พิจารณาว่า url === / redirect to / hello-nextjs
Arthur

คำตอบ:


23

ในnext.jsคุณสามารถเปลี่ยนเส้นทางหลังจากหน้าโหลดโดยใช้Routerอดีต:

import Router from 'next/router'

componentDidMount(){
    const {pathname} = Router
    if(pathname == '/' ){
       Router.push('/hello-nextjs')
    }
}

หรือด้วยตะขอ:

import React, { useEffect } from "react";
...
useEffect(() => {
   const {pathname} = Router
   if(pathname == '/' ){
       Router.push('/hello-nextjs')
   }
 });

ฉันจะทำอย่างไรกับ React hooks?
Tessaracter

โดยไม่ใช้คลาส
Tessaracter

2
อัปเดตคำตอบ @Tessaracter แล้ว
Nico

2
แล้ว SSR ล่ะ? หน้าแรกเริ่มกระพริบด้วยวิธีนี้
Eric Burel

@EricBurel OP ถามอย่างชัดเจนว่า "เมื่อผู้ใช้โหลดหน้าเว็บ" btw ตรวจสอบgithub.com/zeit/next.js/issues/649
Nico

16

มีสามวิธี

1.Redirect กับเหตุการณ์หรือฟังก์ชั่น:

import Router from 'next/router';

<button type="button" onClick={() => Router.push('/myroute')} />

2.Redirect กับ hooks:

import Router , {useRouter}  from 'next/router';

const router = useRouter()

<button type="button" onClick={() => router.push('/myroute')} />

3.Redirect พร้อมลิงค์:

ขึ้นอยู่กับเอกสารของ Nextjs <a>แท็กนั้นมีความจำเป็นในลิงค์สำหรับสิ่งต่างๆเช่นเปิดในแท็บใหม่!

import Link from 'next/link';

<Link href="/myroute">
   <a>myroute</a>
</Link>

มีบางตัวเลือกอื่น ๆ asPathสำหรับการกำหนดเส้นทางเซิร์ฟเวอร์ซึ่งเป็นเป็น ในแนวทางที่อธิบายไว้ทั้งหมดคุณสามารถเพิ่ม asPath เพื่อเปลี่ยนเส้นทางทั้งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์


Hi! คุณสามารถดูวิธีแก้ปัญหาของฉัน
Arthur

นี่เป็นวิธีการที่จำเป็น การเปลี่ยนเส้นทางจากการกระทำของผู้ใช้เป็นไปตามปกติ แต่ไม่ได้ขึ้นอยู่กับเงื่อนไขในการโหลดหน้าเว็บตามที่ระบุในคำถาม
Eric Burel

ฉันไม่ได้รับสิ่งที่คุณหมายถึง!
Afsanefda

คำถามเกี่ยวกับการเปลี่ยนเส้นทางโดยอัตโนมัติขึ้นอยู่กับชื่อเส้นทางในปัจจุบัน คำตอบของคุณถูกต้อง แต่ไม่สามารถใช้ได้ในบริบทนี้: พวกเขาทั้งหมดต้องการให้ผู้ใช้คลิก
Eric Burel

@EricBurel ใช่นี่ไม่ใช่สิ่งที่ฉันต้องการคำตอบนี้ไม่ได้แก้คำถามของฉัน
Arthur

3

@ คำตอบของ Nico แก้ปัญหาเมื่อคุณใช้คลาส

หากคุณใช้ฟังก์ชั่นคุณจะไม่สามารถใช้งานcomponentDidMountได้ คุณสามารถใช้ React Hooks useEffectแทนได้


import React, {useEffect} from 'react';

export default function App() {
  const classes = useStyles();

  useEffect(() => { 
    const {pathname} = Router
    if(pathname == '/' ){
      Router.push('/templates/mainpage1')
    }  
  }
  , []);
  return (
    null
  )
}

ในปี 2019 React แนะนำ hooks ซึ่งเร็วกว่าและมีประสิทธิภาพมากกว่าคลาส


ปัญหานี้อธิบายถึงสิ่งที่ฉันต้องการผลลัพธ์
Arthur

@Arthur โอ้ แต่คำถามของคุณไม่ได้พูดอย่างนั้น คำตอบโดย @Nico และของฉันเหมือนกันทุกประการและเป็นคำตอบ<Switch>ที่คุณใช้React-routerแทน แม้<Switch>จะไม่ได้ระบุรหัสสถานะ 303, 302 เพียงแค่เปลี่ยนเส้นทาง
Tessaracter

ฉันคิดว่าที่นี่พูดถึงเช่นกัน เพิ่งรู้ว่า NextJS ไม่ได้ตั้งรหัสสถานะใด ๆ github.com/zeit/next.js/issues/9443
Tessaracter

โปรดลบชั้นเรียน มันไม่มีประโยชน์ที่นี่
Pushp Singh

3

ตัวอย่างกึ่งทางการ

ตัวอย่างการเปลี่ยนเส้นทางในwith-cookie-auth getInitialPropsฉันไม่แน่ใจว่าเป็นรูปแบบที่ถูกต้องหรือยัง แต่นี่คือรหัส:

Profile.getInitialProps = async ctx => {
  const { token } = nextCookie(ctx)
  const apiUrl = getHost(ctx.req) + '/api/profile'

  const redirectOnError = () =>
    typeof window !== 'undefined'
      ? Router.push('/login')
      : ctx.res.writeHead(302, { Location: '/login' }).end()

  try {
    const response = await fetch(apiUrl, {
      credentials: 'include',
      headers: {
        Authorization: JSON.stringify({ token }),
      },
    })

    if (response.ok) {
      const js = await response.json()
      console.log('js', js)
      return js
    } else {
      // https://github.com/developit/unfetch#caveats
      return await redirectOnError()
    }
  } catch (error) {
    // Implementation or Network error
    return redirectOnError()
  }
}

จัดการทั้งฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ การfetchเรียกเป็นสิ่งที่ได้รับโทเค็นการรับรองความถูกต้องจริงคุณอาจต้องการสรุปแค็ปซูลนี้ในฟังก์ชั่นแยกต่างหาก

สิ่งที่ฉันจะแนะนำแทน

 1. เปลี่ยนเส้นทางการแสดงผลฝั่งเซิร์ฟเวอร์ (หลีกเลี่ยงการกะพริบระหว่าง SSR)

นี่เป็นกรณีที่พบบ่อยที่สุด คุณต้องการเปลี่ยนเส้นทาง ณ จุดนี้เพื่อหลีกเลี่ยงการกระพริบหน้าเริ่มต้นในการโหลดครั้งแรก

MyApp.getInitialProps = async appContext => {
    const currentUser = await getCurrentUser(); // define this beforehand
    const appProps = await App.getInitialProps(appContext);
    // check that we are in SSR mode (NOT static and NOT client-side)
    if (typeof window === "undefined" && appContext.ctx.res.writeHead) {
      if (!currentUser && !isPublicRoute(appContext.router.pathname)) {
          appContext.ctx.res.writeHead(302, { Location: "/account/login" });
          appContext.ctx.res.end();
      }
    }
    return { ...appProps, currentUser };
  };
 2. Redirect ใน componentDidMount (มีประโยชน์เมื่อปิดใช้งาน SSR เช่นในโหมดคงที่)

นี่คือทางเลือกสำหรับการเรนเดอร์ฝั่งไคลเอ็นต์

  componentDidMount() {
    const { currentUser, router } = this.props;
    if (!currentUser && !isPublicRoute(router.pathname)) {
      Router.push("/account/login");
    }
  }

ฉันไม่สามารถหลีกเลี่ยงการกระพริบหน้าเริ่มต้นในโหมดคงที่เพิ่มจุดนี้เพราะคุณไม่สามารถเปลี่ยนเส้นทางในระหว่างการสร้างคงที่ แต่ดูเหมือนว่าดีกว่าวิธีปกติ ฉันจะพยายามแก้ไขเมื่อฉันดำเนินการไป

ตัวอย่างเต็มอยู่ที่นี่

ปัญหาที่เกี่ยวข้องซึ่งจบลงด้วยการตอบลูกค้าเพียงเศร้า


1

redirect-to.ts

import Router from "next/router";

export default function redirectTo(
  destination: any,
  { res, status }: any = {}
): void {
  if (res) {
    res.writeHead(status || 302, { Location: destination });
    res.end();
  } else if (destination[0] === "/" && destination[1] !== "/") {
    Router.push(destination);
  } else {
    window.location = destination;
  }
}

_app.tsx

import App, {AppContext} from 'next/app'
import Router from "next/router"
import React from 'react'
import redirectTo from "../utils/redirect-to"


export default class MyApp extends App {
  public static async getInitialProps({Component, ctx}: AppContext): Promise<{pageProps: {}}> {
    let pageProps = {};

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }

    if (ctx.pathname === "" || ctx.pathname === "/_error") {
      redirectTo("/hello-next-js", { res: ctx.res, status: 301 }); <== Redirect-To
      return {pageProps};
    }

    return {pageProps};
  }

  render() {
    const {Component, pageProps} = this.props;
    return <Component {...pageProps}/>
  }
}

2
นี่ไม่ควรเป็นคำตอบที่ยอมรับได้ ตามนี้github.com/zeit/next.js/issues/4931#issuecomment-512787861getInitialPropsคุณไม่ควรเปลี่ยนเส้นทางใน @ Afsanefda ควรเป็นคำตอบที่ยอมรับได้ และคุณกำลังใช้ next.js คุณไม่จำเป็นต้องตอบสนองเราเตอร์เพื่อจัดเส้นทาง ถัดไปจัดการแล้วโดยค่าเริ่มต้น
rotimi ที่ดีที่สุด

3
@ rotimi ดีที่สุดเท่าที่ฉันจำได้ฉันเอาโค้ดนี้จากตัวอย่าง next.js นอกจากนี้ฉันไม่ได้ใช้ react-router มันถูกนำเสนอเป็นตัวอย่างของสิ่งที่ฉันต้องการได้รับ
Arthur

2
นี่เป็นคำตอบที่ถูกต้อง แต่ใช้ SSR เท่านั้น มันจะไม่เปลี่ยนเส้นทางในแอพแบบคงที่ แก้ไข: จริง ๆ แล้วคุณจะเพิ่ม Router.push แต่ฝั่งไคลเอ็นต์Router.pushควรเข้าสู่วงจรชีวิตของส่วนประกอบแทน
Eric Burel

1

ฉันได้ใช้งานฟังก์ชั่นนี้ในNext.JSแอพของฉันโดยกำหนดหน้ารูทซึ่งทำหน้าที่เปลี่ยนเส้นทางฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ นี่คือรหัสสำหรับหน้ารูต:

import { useEffect } from "react";
import Router from "next/router";

const redirectTo = "/hello-nextjs";

const RootPage = () => {
  useEffect(() => Router.push(redirectTo));
  return null;
};
RootPage.getInitialProps = (ctx) => {
  if (ctx.req) {
    ctx.res.writeHead(302, { Location: redirectTo });
    ctx.res.end();
  }
};

export default RootPage;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.