Pass prop ke setiap halaman di Next.js dengan getInitialProps menggunakan Typescript

10

Saya memiliki kasus ketika saya perlu tahu apakah pengguna masuk atau tidak sebelum halaman diberikan sisi server dan dikirim kembali kepada saya menggunakan Next.js untuk menghindari perubahan flicker di UI.

Saya bisa mengetahui cara mencegah pengguna mengakses beberapa halaman jika dia sudah masuk menggunakan komponen HOC ini ...

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;
};

Dan ini bekerja dengan baik.

Sekarang, bagaimana saya bisa membangun komponen HOC yang serupa untuk membungkusnya katakanlah "_app.tsx" sehingga saya bisa meneruskan prop "userAuthenticated" ke setiap halaman dengan mendapatkan token dan mencari tahu apakah kadaluwarsa atau tidak dan berdasarkan pada prop yang saya dapat menunjukkan UI yang tepat kepada pengguna tanpa efek berkedip yang mengganggu?

Saya harap Anda dapat membantu saya dengan hal itu, saya mencoba melakukannya dengan cara yang sama seperti saya membangun HOC di atas, tetapi saya tidak bisa melakukannya, terutama karena Typacrip tidak membuat ini lebih mudah dengan kesalahan aneh :(


Edit == ==========================================

Saya dapat membuat komponen HOC tersebut dan meneruskan pro userAuthenticatedke setiap halaman seperti ini ...

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;
};

Namun saya harus membungkus setiap halaman dengan HOC ini untuk meneruskan prop userAuthenticatedke tata letak global yang saya miliki, karena saya tidak dapat membungkus komponen kelas "_app.tsx" dengan itu, selalu memberi saya Kesalahan. ..

Ini bekerja ...

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

Tapi ini tidak ...

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

Jadi agak menjengkelkan karena harus melakukan ini pada setiap halaman, dan kemudian meneruskan prop ke tata letak global di setiap halaman daripada hanya melakukannya sekali di "_app.tsx".

Saya menduga alasannya mungkin karena "_app.tsx" adalah komponen kelas dan bukan komponen fungsi seperti sisa halaman? Saya tidak tahu, saya hanya menebak.

Ada bantuan dengan itu?

Rubi
sumber

Jawaban:

5

Bagi Anda yang mungkin menemukan masalah yang sama, saya bisa menyelesaikan ini sebagai berikut ...

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);

Seperti yang Anda lihat saya memposting seluruh komponen _app.tsx sehingga Anda dapat melihat paket-paket yang saya gunakan.

Saya menggunakan next-redux-wrapperdan styled-componentsdengan naskah.

Aku harus membuat appContextdi gitInitialPropssebagai jenis any, selain itu tidak akan bekerja. Jadi, jika Anda memiliki typesaran yang lebih baik, silakan beri tahu saya. Saya mencoba menggunakan tipenya NextPageContext, tetapi itu tidak berhasil dalam kasus ini karena beberapa alasan.

Dan dengan solusi itu, saya bisa mengetahui apakah pengguna mengotentikasi atau tidak dan meneruskan prop ke tata letak global sehingga saya dapat menggunakannya di setiap halaman dan tanpa harus melakukannya halaman per halaman dan itu juga menguntungkan jika Anda tidak ingin header dan footer di-render setiap kali jika mereka harus bergantung pada userAuthenticatedprop, karena sekarang Anda bisa meletakkan header dan footer di GlobalLayoutkomponen dan masih memiliki userAuthenticatedprop yang Anda inginkan: D

Rubi
sumber
Komentar saya tidak terkait dengan posting Anda dan jawaban Anda. Saya ingin mengatakan, ReactJSikuti Pemrograman Fungsional atau OOP, tapi saya melihat pola pikir pengembangan backend dalam kode Anda dengan pemikiran OOP. Mewarisi kelas baru dari komponen lain! 😐 Saya tidak melihat seperti itu sebelumnya.
AmerllicA
1
@AmerllicA Saya mengerti apa yang Anda katakan, saya baru saja mencoba semuanya. Masalahnya adalah dengan SSR, jika saya bekerja dengan "Buat React App" dan melakukan "Rendering Sisi Klien" Saya tidak akan melakukan itu sama sekali, tapi saya tidak bisa membuatnya bekerja dengan cara lain dengan SSR, itu bahkan merupakan cara yang disarankan oleh Next.js.
Ruby