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

标签 javascript reactjs typescript next.js

在这种情况下,我需要在页面渲染到服务器端并使用 Next.js 发送回我之前知道用户是否已登录,以避免 UI 中发生闪烁更改。

如果用户已经使用此 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”,这样我就可以通过获取 token 将“userAuthenticated”属性传递到每个页面并确定它是否已过期基于该 Prop ,我可以向用户显示正确的 UI,而不会出现烦人的闪烁效果?

我希望你能帮助我,我尝试按照构建上述 HOC 的方式进行操作,但我做不到,尤其是 Typescript 并没有因为它的奇怪错误而使这变得更容易:(


编辑==============================================

我能够创建这样的 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;
};

但是,我必须用这个 HOC 包裹每一页才能传递 Prop userAuthenticated到我拥有的全局布局,因为我无法用它包装“_app.tsx”类组件,它总是给我一个错误......

这有效...

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

但这并不...

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

因此,必须对每个页面执行此操作,然后将属性传递给每个页面中的全局布局,而不是只在“_app.tsx”中执行一次,这有点烦人。

我猜原因可能是因为“_app.tsx”是一个类组件,而不是像其他页面一样的函数组件? 我不知道,我只是猜测。

有什么帮助吗?

最佳答案

对于那些可能遇到同样问题的人,我能够按照以下方式解决此问题...

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-wrapperstyled-components 与 Typescript 结合使用。

我必须将 gitInitialProps 中的 appContext 设置为 any 类型,否则它将无法工作。因此,如果您有更好的type建议,请告诉我。我尝试使用 NextPageContext 类型,但由于某种原因在这种情况下不起作用。

通过该解决方案,我能够了解用户是否经过身份验证,并将 Prop 传递给全局布局,以便我可以在每个页面中使用它,而不必逐页进行操作,而且如果您不希望每次都渲染页眉和页脚(如果它们必须依赖于 userAuthenticated 属性),那么这是有好处的,因为现在您可以将页眉和页脚放在 GlobalLayout 组件,并且仍然可以使用 userAuthenticated 属性:D

关于javascript - 使用 Typescript 通过 getInitialProps 将 prop 传递到 Next.js 中的每个页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59670774/

相关文章:

javascript - 过滤谷歌地图范围内的标记

javascript - axios 获取参数

reactjs - 为什么我的 React onChange 方法与 enzyme containsAllMatchingElements 测试中的箭头函数不匹配

类似 jQuery 的方式查找/选择 React 元素数组

javascript - 如果过滤器在 Material 表 Angular 中没有结果,如何显示 "no records"

typescript - 使用接口(interface)的所有可选字段创建子接口(interface)

javascript - 保持按钮处于选中状态

javascript - 如何在 React 中处理已 checkin 的输入类型 ='checkbox'?

reactjs - dotenv : how to set custom path

JavaScript 正则表达式将 DIV 与特定类相匹配