Übergeben Sie prop mit getInitialProps mithilfe von Typescript an jede einzelne Seite in Next.js

10

Ich habe einen Fall, in dem ich wissen muss, ob der Benutzer angemeldet ist oder nicht, bevor die Seite serverseitig gerendert und mit Next.js an mich zurückgesendet wird, um flimmernde Änderungen in der Benutzeroberfläche zu vermeiden.

Ich konnte herausfinden, wie verhindert werden kann, dass der Benutzer auf einige Seiten zugreift, wenn er bereits mit dieser HOC-Komponente angemeldet ist ...

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

Und das funktioniert super.

Wie kann ich nun eine ähnliche HOC-Komponente erstellen, um sie zu umschließen? Sagen wir "_app.tsx", damit ich "userAuthenticated" an jede einzelne Seite übergeben kann, indem ich das Token erhalte und herausfinde, ob es abgelaufen ist oder nicht und basierend auf diese Requisite kann ich dem Benutzer die richtige Benutzeroberfläche zeigen, ohne diesen störenden Flackereffekt?

Ich hoffe, Sie können mir dabei helfen. Ich habe versucht, es auf die gleiche Weise zu tun, wie ich das obige HOC erstellt habe, aber ich konnte es nicht tun, insbesondere, dass Typescript dies mit seinen seltsamen Fehlern nicht einfacher macht :(


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

Ich konnte eine solche HOC-Komponente erstellen und den Profi wie folgt userAuthenticatedan jede Seite weitergeben ...

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

Ich musste jedoch jede einzelne Seite mit diesem HOC userAuthenticatedumbrechen , um die Requisite an das globale Layout weiterzugeben, das ich habe, da ich die Klassenkomponente "_app.tsx" nicht damit umschließen konnte, gibt es immer einen Fehler. ..

Das funktioniert ...

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

Aber das tut es nicht ...

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

Es ist also etwas ärgerlich, dies für jede einzelne Seite tun zu müssen und dann die Requisite an das globale Layout auf jeder Seite weiterzugeben, anstatt es nur einmal in der "_app.tsx" zu tun.

Ich vermute, der Grund könnte sein, dass "_app.tsx" eine Klassenkomponente und keine Funktionskomponente wie der Rest der Seiten ist. Ich weiß nicht, ich rate nur.

Hilfe dabei?

Rubin
quelle

Antworten:

5

Für diejenigen unter Ihnen, die möglicherweise auf dasselbe Problem stoßen, konnte ich dieses Problem wie folgt lösen ...

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

Wie Sie sehen, habe ich die gesamte _app.tsx-Komponente veröffentlicht, damit Sie die von mir verwendeten Pakete sehen können.

Ich benutze next-redux-wrapperund styled-componentsmit Typescript.

Ich musste das appContextin gitInitialPropsals Typ machen any, sonst wird es nicht funktionieren. Wenn Sie also einen besseren typeVorschlag haben, lassen Sie es mich bitte wissen. Ich habe versucht, den Typ zu verwenden NextPageContext, aber das hat in diesem Fall aus irgendeinem Grund nicht funktioniert.

Und mit dieser Lösung konnte ich herausfinden, ob sich der Benutzer authentifiziert hat oder nicht, und eine Requisite an das globale Layout übergeben, damit ich sie auf jeder Seite verwenden kann, ohne sie Seite für Seite ausführen zu müssen, und das ist auch für den Fall von Vorteil Sie möchten nicht, dass die Kopf- und Fußzeile jedes Mal gerendert wird, wenn sie von der userAuthenticatedRequisite abhängen müssen , da Sie jetzt einfach die Kopf- und Fußzeile in die GlobalLayoutKomponente einfügen können und die userAuthenticatedRequisite weiterhin zur Verfügung haben: D.

Rubin
quelle
Mein Kommentar bezieht sich nicht auf Ihren Beitrag und Ihre Antwort. Ich möchte sagen, ReactJSfolgt weder der funktionalen Programmierung noch der OOP, aber ich sehe die Denkweise der Backend-Entwicklung in Ihrem Code mit OOP-Gedanken. Erben Sie eine neue Klasse von einer anderen Komponente! »Ich habe es vorher nicht so gesehen.
AmerllicA
1
@AmerllicA Ich verstehe, was du sagst, ich habe einfach alles versucht. Das Problem ist mit SSR. Wenn ich mit "Create React App" arbeite und "Client-Side Rendering" mache, würde ich das überhaupt nicht tun, aber ich könnte es nicht dazu bringen, mit SSR anders zu funktionieren ist sogar der von Next.js empfohlene Weg.
Ruby