ค้นหาเว็บไซต์

reactjs - เป็นไปได้ไหมที่จะทำการเปลี่ยนเส้นทางฝั่งเซิร์ฟเวอร์พร้อมการปรับตัวระหว่างประเทศใน Next.js?


ดังนั้นฉันมีแอป Next.js ขนาดเล็ก ซึ่งมีหน้าเพียงสำหรับผู้ใช้ที่ล็อกอินแล้ว ไม่มีอะไรพิเศษที่นั่น เพื่อจัดการเส้นทางที่ต้องมีการยืนยันตัวตน ฉันเขียน HOC (เรียกว่า withAuth) ที่จะเปลี่ยนเส้นทางบนเซิร์ฟเวอร์ เพื่อหลีกเลี่ยงการกระพริบของหน้าบนฝั่งลูกค้า มันทำงานได้ดีจนถึงตอนนี้ แต่ฉันมีปัญหาว่าแอปของฉันรองรับหลายภาษา ดังนั้นในปัจจุบันผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังหน้าล็อกอินในภาษาปริยาย (ภาษาอังกฤษ) แต่ฉันต้องการให้เก็บภาษาที่ผู้ใช้เลือกไว้

โชคไม่ดีที่ useRouter() ใช้ได้เฉพาะบนฝั่งไคลเอนต์ ดังนั้นวิธีแรกของฉันจึงไม่สำเร็จ (ฉันควรรู้ดีกว่านี้) นี่คือโค้ดของฉัน:

auth.tsx

import redirect from "../utils/redirect";
import * as React from "react";
import { sendData } from "../utils/sendRequest";

export const withAuth = <T extends object>(C: React.ComponentClass<T>) => {
  return class AuthComponent extends React.Component<T> {
    static async getInitialProps({...ctx}: NextPageContext) {
      await sendData('GET','/user').then(response => {
        if (!response || !response.success) {
          redirect(ctx, "/login");
          return {
            user: null,
          };
        }else return {
          user: response.user,
        };
      }).catch(err=>{console.error(err)})

    }

    render() {
      return <C {...this.props} />;
    }
  };
};

redirect.ts

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

const redirect = (context: any, target: string) => {
  let lang = "";
  /*if(!(useRouter.locale == "en")){
    lang = "/"+useRouter.locale; This wont work
  }*/
  if (context.res) {
    context.res.writeHead(303, { Location: /*lang+*/target });
    context.res.end();
  } else {
    Router.replace(/*lang+*/target);
  }
};
export default redirect;

โปรไฟล์.tsx

import * as React from "react";
import { withAuth } from "../components/auth";

//...

class ProfileClass extends React.PureComponent<{}> {
  render() {
    return <Profile />;
  }
}

export default withAuth(ProfileClass);

ฉันรู้สึกขอบคุณสำหรับทุกไอเดียที่จะแก้ปัญหานี้ :) ขอบคุณ!

คำตอบ #1

วิธีแก้ปัญหาด้วย getInitialProps ในหน้า _app

คุณสามารถเข้าถึงอินสแตนซ์ของ router จากออบเจ็กต์ context ของ getInitialProps ซึ่งสามารถเข้าถึง locale ปัจจุบันได้ โปรดทราบว่าออบเจ็กต์ router จะสามารถใช้ได้เฉพาะกับ context ที่ส่งไปยัง _app ของ getInitialProps เท่านั้น แต่จะไม่สามารถใช้ได้กับ context ที่ส่งไปยังหน้าอื่น ๆ

// pages/_app.tsx

static async getInitialProps({ router, ctx }: NextPageContext) {
    const lang = router.locale;

    await sendData('GET','/user').then(response => {
        if (!response || !response.success) {
            redirect(ctx, `${lang}/login`);
            return {
                user: null,
            };
        } else {
            return {
                user: response.user,
            };
        }
    }).catch(err => { console.error(err) })
}

วิธีแก้ปัญหาด้วย getServerSideProps สำหรับหน้าต่าง ๆ

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

ก่อนอื่น เราจะนำ withAuth มาใช้ใหม่และแยกตรรกะสำหรับการดึงผู้ใช้ ซึ่งจะเพิ่มวัตถุ user ไปยัง context และส่งต่อไปยัง getServerSideProps

// /components/auth

export function withAuth(gssp) {
    return async (context: GetServerSidePropsContext) => {
        try {
            const response = await sendData("GET", "/user");
            if (response && response.success) {
                // Add user to context to be used in `getServerSideProps`
                context = { ...context, user: response.user };
            }
        } catch (err) {
            console.error(err);
        }

        return await gssp(context); // Pass `context` and continue on to call `getServerSideProps`
    };
}

จากนั้น withAuth สามารถใช้เพื่อห่อฟังก์ชัน getServerSideProps ในหน้าของคุณ ซึ่งการเปลี่ยนเส้นทางจะเกิดขึ้นหาก user ไม่ได้ถูกตั้งค่า

import * as React from "react";
import { withAuth } from "../components/auth";

export const getServerSideProps: GetServerSideProps = withAuth(async (context: : GetServerSidePropsContext) => {
    if (!context.user) {
        return {
            redirect: {
                destination: `/${context.locale}/login`,
                statusCode: 303
            }
        };
    }

    return {
        props: { 
            user: context.user 
        }
    };
});

class ProfileClass extends React.PureComponent<{}> {
    render() {
        return <Profile />;
    }
}

export default ProfileClass;