ผ่าน prop ไปทุกหน้าใน Next.js ด้วย getInitialProps โดยใช้ Typescript


10

ฉันมีกรณีเมื่อฉันต้องรู้ว่าผู้ใช้เข้าสู่ระบบหรือไม่ก่อนที่จะได้รับการแสดงผลด้านเซิร์ฟเวอร์และส่งกลับมาให้ฉันโดยใช้ Next.js เพื่อหลีกเลี่ยงการเปลี่ยนแปลงวูบวาบใน UI

ฉันสามารถหาวิธีป้องกันไม่ให้ผู้ใช้เข้าถึงบางหน้าได้ถ้าเขาเข้าสู่ระบบแล้วโดยใช้ส่วนประกอบ HOC นี้ ...

export const noAuthenticatedAllowed = (WrappedComponent: NextPage) => {
    const Wrapper = (props: any) => {
        return <WrappedComponent {...props} />;
    };

    Wrapper.getInitialProps = async (ctx: NextPageContext) => {
        let context = {};
        const { AppToken } = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.req) {
                if (!isExpired()) {
                    ctx.res && ctx.res.writeHead(302, { Location: "/" });
                    ctx.res && ctx.res.end();
                }
            }

            if (!isExpired()) {
                context = { ...ctx };
                Router.push("/");
            }
        }

        const componentProps =
            WrappedComponent.getInitialProps &&
            (await WrappedComponent.getInitialProps(ctx));

        return { ...componentProps, context };
    };

    return Wrapper;
};

และใช้งานได้ดี

ตอนนี้ฉันจะสร้างองค์ประกอบ HOC ที่คล้ายกันเพื่อล้อมรอบได้อย่างไรสมมติว่า "_app.tsx" เพื่อให้ฉันสามารถส่ง "userAuthenticated" prop ไปยังทุก ๆ หน้าโดยรับโทเค็นและคิดออกว่ามันหมดอายุหรือไม่และขึ้นอยู่กับ prop นั้นฉันสามารถแสดง UI ที่เหมาะสมให้กับผู้ใช้โดยไม่มีเอฟเฟกต์กะพริบที่น่ารำคาญ?

ฉันหวังว่าคุณสามารถช่วยฉันได้ฉันพยายามทำแบบเดียวกับที่ฉันสร้าง HOC ข้างต้น แต่ฉันทำไม่ได้โดยเฉพาะที่ typescript ไม่ได้ทำให้สิ่งนี้ง่ายขึ้นด้วยข้อผิดพลาดแปลก ๆ :(


แก้ไข == ==========================================

ฉันสามารถสร้างองค์ประกอบ HOC ดังกล่าวและส่งต่อโปรuserAuthenticatedไปยังแต่ละหน้าเช่นนี้ ...

export const isAuthenticated = (WrappedComponent: NextPage) => {
    const Wrapper = (props: any) => {
        return <WrappedComponent {...props} />;
    };

    Wrapper.getInitialProps = async (ctx: NextPageContext) => {
        let userAuthenticated = false;

        const { AppToken} = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.req) {
                if (!isExpired()) {
                    // ctx.res && ctx.res.writeHead(302, { Location: "/" });
                    // ctx.res && ctx.res.end();
                    userAuthenticated = true;
                }
            }

            if (!isExpired()) {
                userAuthenticated = true;
            }
        }

        const componentProps =
            WrappedComponent.getInitialProps &&
            (await WrappedComponent.getInitialProps(ctx));

        return { ...componentProps, userAuthenticated };
    };

    return Wrapper;
};

อย่างไรก็ตามฉันต้องห่อทุก ๆ หน้าด้วย HOC นี้เพื่อส่งเสาuserAuthenticatedไปยังเลย์เอาต์ทั่วโลกที่ฉันมีเพราะฉันไม่สามารถรวมองค์ประกอบคลาส "_app.tsx" ไว้ได้มันทำให้ฉันมีข้อผิดพลาดอยู่เสมอ ..

มันใช้งานได้ ...

export default isAuthenticated(Home);
export default isAuthenticated(about);

แต่นี่ไม่ใช่ ...

export default withRedux(configureStore)(isAuthenticated(MyApp));

ดังนั้นมันจึงน่ารำคาญนิดหน่อยที่ต้องทำสิ่งนี้กับทุก ๆ หน้าแล้วส่งผ่านเสาไปยังเลย์เอาต์ทั่วโลกในแต่ละหน้าแทนที่จะทำเพียงครั้งเดียวใน "_app.tsx"

ฉันคาดเดาสาเหตุอาจเป็นเพราะ "_app.tsx" เป็นองค์ประกอบของชั้นเรียนและไม่ใช่องค์ประกอบของฟังก์ชั่นเช่นส่วนที่เหลือของหน้า? ฉันไม่รู้ฉันแค่เดา

มีความช่วยเหลือไหม?

คำตอบ:


5

สำหรับบรรดาของคุณที่อาจเจอปัญหาเดียวกันฉันสามารถแก้ปัญหาดังต่อไปนี้ ...

import React from "react";
import App from "next/app";
import { Store } from "redux";
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import { ThemeProvider } from "styled-components";
import GlobalLayout from "../components/layout/GlobalLayout";
import { configureStore } from "../store/configureStore";
import { GlobalStyle } from "../styles/global";
import { ToastifyStyle } from "../styles/toastify";
import nextCookie from "next-cookies";
import jwt_decode from "jwt-decode";

 export interface MetadataObj {
   [key: string]: any;
 }

const theme = {
    color1: "#00CC99",
    color2: "#CC0000"
};

export type ThemeType = typeof theme;

interface Iprops {
    store: Store;
    userAuthenticated: boolean;
}

class MyApp extends App<Iprops> {
    // Only uncomment this method if you have blocking data requirements for
    // every single page in your application. This disables the ability to
    // perform automatic static optimization, causing every page in your app to
    // be server-side rendered.

    static async getInitialProps({ Component, ctx }: any) {
        let userAuthenticated = false;

        const { AppToken } = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.isServer) {
                if (!isExpired()) {
                    userAuthenticated = true;
                }
            }

            if (!isExpired()) {
                userAuthenticated = true;
            }
        }

        return {
            pageProps: Component.getInitialProps
                ? await Component.getInitialProps(ctx)
                : {},
            userAuthenticated: userAuthenticated
        };
    }

    render() {
        const { Component, pageProps, store, userAuthenticated } = this.props;
        return (
            <Provider store={store}>
                <ThemeProvider theme={theme}>
                    <>
                        <GlobalStyle />
                        <ToastifyStyle />
                        <GlobalLayout userAuthenticated={userAuthenticated}>
                            <Component {...pageProps} />
                        </GlobalLayout>
                    </>
                </ThemeProvider>
            </Provider>
        );
    }
}

export default withRedux(configureStore)(MyApp);

ตามที่คุณเห็นฉันโพสต์ส่วนประกอบ _app.tsx ทั้งหมดเพื่อให้คุณสามารถดูแพ็คเกจที่ฉันใช้

ฉันใช้next-redux-wrapperและstyled-componentsด้วย typescript

ผมต้องทำให้appContextในgitInitialPropsขณะที่ประเภทanyมิฉะนั้นมันจะไม่ทำงาน ดังนั้นหากคุณมีtypeข้อเสนอแนะที่ดีกว่าโปรดแจ้งให้เราทราบ ฉันพยายามใช้ชนิดNextPageContextแต่ไม่ได้ทำงานในกรณีนี้ด้วยเหตุผลบางอย่าง

และด้วยวิธีการแก้ปัญหานั้นฉันสามารถรับรู้ว่าผู้ใช้รับรองความถูกต้องหรือไม่และส่งเสาไปยังเลย์เอาต์ทั่วโลกเพื่อให้ฉันสามารถใช้งานได้ในทุกหน้าและไม่ต้องทำหน้าต่อหน้าและมันก็เป็นประโยชน์เช่นกัน คุณไม่ต้องการให้ส่วนหัวและส่วนท้ายได้รับการเรนเดอร์ทุกครั้งหากพวกเขาต้องพึ่งพาuserAuthenticatedเสาเพราะตอนนี้คุณสามารถใส่หัวกระดาษและท้ายกระดาษในGlobalLayoutส่วนประกอบและยังคงมีuserAuthenticatedเสาอยู่ในมือคุณ: D


ความคิดเห็นของฉันไม่เกี่ยวข้องกับโพสต์และคำตอบของคุณ ฉันต้องการพูดReactJSตามฟังก์ชั่นการเขียนโปรแกรมหรือ OOP แต่ฉันเห็นความคิดของการพัฒนาแบ็กเอนด์ในรหัสคุณด้วยความคิด OOP รับคลาสใหม่จากส่วนประกอบอื่น! 😐ฉันไม่เคยเห็นแบบนี้มาก่อน
AmerllicA

1
@ AmerllicA ฉันได้สิ่งที่คุณพูดฉันแค่ลองทุกอย่าง ปัญหาเกิดขึ้นกับ SSR ถ้าฉันทำงานกับ "สร้างแอป React" และทำ "การแสดงผลฝั่งไคลเอ็นต์" ฉันจะไม่ทำอย่างนั้น แต่ฉันไม่สามารถทำให้มันทำงานด้วย SSR ได้ แม้แต่วิธีที่แนะนำโดย Next.js
ทับทิม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.