Передайте опору на кожну окрему сторінку в Next.js за допомогою getInitialProps за допомогою Typescript


10

У мене є випадок, коли мені потрібно знати, чи користувач увійшов чи ні, перш ніж сторінка буде надана на сервер і повернена мені за допомогою Next.js, щоб уникнути зміни мерехтіння в інтерфейсі користувача.

Я зміг зрозуміти, як не допустити доступу користувача до деяких сторінок, якщо він уже ввійшов у систему за допомогою цього компонента 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", щоб я міг передати "userAuthentication" опору на кожну окрему сторінку, отримавши маркер і зрозуміти, чи закінчився термін його дії чи ні, і на основі що я можу показати користувачеві належний інтерфейс користувача без цього дратівливого мерехтливого ефекту?

Я сподіваюся, що ви можете мені допомогти в цьому, я намагався зробити це так само, як я створив вищевказаний HOC, але я не міг цього зробити, тим більше, що Typescript не робить це легше своїми дивними помилками :(


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

Я зміг створити такий компонент 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-componentsTypescript.

Я повинен був зробити appContextпо прибуттю в gitInitialPropsякості типу any, в іншому випадку він не буде працювати. Тож якщо у вас є краща typeпропозиція, будь ласка, повідомте мене про це. Я намагався використати тип NextPageContext, але це не спрацювало в цьому випадку чомусь.

І з цим рішенням я зміг дізнатися, чи користувач перевірив автентичність чи ні, і передав опору глобальному макеті, щоб я міг використовувати його на кожній сторінці і без того, щоб робити це на сторінці, і це також вигідно у випадку ви не хочете, щоб заголовок і колонтитул отримували щоразу, якщо вони залежати від userAuthenticatedопори, тому що тепер ви можете просто помістити заголовок і колонтитул в GlobalLayoutкомпонент і все ще мати userAuthenticatedреквізит у вашому розпорядженні: D


Мій коментар не пов'язаний з вашою публікацією та вашою відповіддю. Я хочу сказати, що ReactJSслідкуйте за функціональним програмуванням, ані не за допомогою OOP, але я бачу, як у вас кодується думка про розвиток програми з думками OOP. Успадкуйте новий клас з іншого компонента! Я раніше такого не бачив.
AmerllicA

1
@AmerllicA Я розумію, що ти кажеш, я просто спробував усе. Проблема полягає в SSR, якщо я працюю зі "Create React App" і роблю "Client-Side Rendering", я б цього не робив, але я не міг змусити його працювати іншим способом з SSR, є навіть рекомендованим способом Next.js.
Рубін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.