简体   繁体   English

使用 Typescript 使用 getInitialProps 将 prop 传递到 Next.js 中的每个页面

[英]Pass prop to every single page in Next.js with getInitialProps using Typescript

I have a case when I need to know if the user is logged in or not before the page gets rendered server-side and sent back to me using Next.js to avoid having flicker changes in the UI.我有一个案例,当我需要在页面被呈现服务器端之前知道用户是否登录并使用 Next.js 发送回我以避免 UI 中的闪烁更改时。

I was able to figure out how to prevent the user from accessing some pages if he is already logged in using this HOC component ...如果用户已经使用此 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;
};

And this works great.这很好用。

Now, how can I build a similar HOC component to wrap it around let's say "_app.tsx" so that I can pass "userAuthenticated" prop to every single page by getting the token and figure out if it is expired or not and based on that prop I can show the proper UI to the user without that annoying flickering effect?现在,我如何构建一个类似的 HOC 组件来包装它,让我们说“_app.tsx”,这样我就可以通过获取令牌将“userAuthenticated”属性传递给每个页面,并确定它是否过期并基于那个道具我可以向用户显示正确的用户界面而不会有烦人的闪烁效果?

I hope you can help me with that, I tried to do it the same way I built the above HOC, but I couldn't do it, especially that Typescript doesn't make this any easier with its weird errors :(我希望你能帮我解决这个问题,我试着用我构建上述 HOC 的方式来做,但我做不到,尤其是 Typescript 并没有因为它的奇怪错误而使这变得更容易:(


Edit ============================================编辑 ============================================

I was able to create such HOC component and pass down the pro userAuthenticated to each page like this ...我能够创建这样的 HOC 组件并将 pro 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;
};

However I had to wrap every single page with this HOC in order to pass down the prop userAuthenticated to the global layout that I have, because I couldn't wrap the "_app.tsx" class component with it, it always gives me an Error ...但是,我必须用这个 HOC 包装每个页面,以便将道具userAuthenticated给我拥有的全局布局,因为我无法用它包装“_app.tsx”类组件,它总是给我一个错误...

This works ...这工作...

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

But this doesn't ...但这不...

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

So it is a bit annoying having to do this to each and every page, and then pass down the prop to the global layout in each and every page instead of just do it once in the "_app.tsx".因此,必须对每个页面都执行此操作,然后将 prop 传递给每个页面中的全局布局,而不是仅在“_app.tsx”中执行一次,这有点烦人。

I'm guessing the reason may be because "_app.tsx" is a class component and not a function component like the rest of the pages?我猜原因可能是因为“_app.tsx”是一个类组件而不是像其他页面那样的功能组件? I don't know, I'm just guessing.我不知道,我只是猜测。

Any help with that?有什么帮助吗?

For those of you who might come across the same issue, I was able to solve this as following ...对于那些可能遇到同样问题的人,我能够解决这个问题如下......

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

As you see I posted the whole _app.tsx component so that you can see the packages that I'm using.如您所见,我发布了整个 _app.tsx 组件,以便您可以看到我正在使用的包。

I'm using next-redux-wrapper and styled-components with Typescript.我正在使用带有 Typescript 的next-redux-wrapperstyled-components

I had to make the appContext in gitInitialProps as of type any , otherwise it won't work.我必须将appContext中的gitInitialProps设为any类型,否则它将无法工作。 So if you have a better type suggestion please let me know.因此,如果您有更好的type建议,请告诉我。 I tried to use the type NextPageContext , but that didn't work in this case for some reason.我尝试使用NextPageContext类型,但由于某种原因在这种情况下不起作用。

And with that solution, I was able to get to know if the user authenticated or not and pass a prop to the global layout so that I can use it in every page and without having to do it page per page and that also benefits in case you don't want the header and the footer to get rendered every time if they have to depend on on the userAuthenticated prop, because now you can just put the header and the footer in the GlobalLayout component and still have the userAuthenticated prop at your disposal :D使用该解决方案,我能够了解用户是否已通过身份验证并将 prop 传递给全局布局,以便我可以在每个页面中使用它,而不必逐页进行,这也有利于以防万一如果页眉和页脚必须依赖于userAuthenticated道具,您不希望它们每次都被渲染,因为现在您只需将页眉和页脚放在GlobalLayout组件中,并且仍然可以使用userAuthenticated道具:D

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM