Typescriptを使用してgetInitialPropsでNext.jsのすべての単一ページに小道具を渡す


10

UIがちらつくのを避けるために、ページがサーバー側でレンダリングされ、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」と言って、トークンを取得して「userAuthenticated」プロップをすべてのページに渡し、トークンが期限切れかどうか、およびその小道具は、ユーザーに適切なUIを表示できますが、そのちらつき効果はありませんか?

私は上記の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;
};

ただしuserAuthenticated、「_ app.tsx」クラスコンポーネントをラップできないため、プロップをグローバルレイアウトに渡すために、このHOCですべてのページをラップする必要があり、常にエラーが発生しました。 ..

これは動作します...

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コンポーネント全体を投稿して、使用しているパッケージを確認できるようにしました。

Typescript を使用next-redux-wrapperstyled-componentsています。

私はappContextinをin gitInitialPropsanyにする必要がありました。そうしないと機能しません。より良いtype提案がある場合はお知らせください。typeを使用しようとしましたNextPageContextが、何らかの理由でこの場合は機能しませんでした。

そしてその解決策で、ユーザーが認証されているかどうかを知り、小道具をグローバルレイアウトに渡して、すべてのページで使用できるようにしました。userAuthenticatedプロップに依存する必要がある場合にヘッダーとフッターが毎回レンダリングされるのは望ましくありません。これは、ヘッダーとフッターをGlobalLayoutコンポーネントに配置するだけで、userAuthenticatedプロップを自由に使用できるためです。


私のコメントはあなたの投稿や回答とは関係ありません。私は言いたいのですが、ReactJS関数型プログラミングやOOPに従いますが、OOPの考えを使用したコードでのバックエンド開発の考え方を理解しています。別のコンポーネントから新しいクラスを継承します!before今まで見たことがなかった。
AmerllicA

1
@AmerllicA私はあなたが言っていることを理解し、私はすべてを試しました。問題はSSRにあります。「React Appの作成」で「クライアントサイドレンダリング」を実行している場合、それはまったくできませんが、SSRで他の方法で機能させることはできません。 Next.jsによる推奨方法です。
ルビー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.