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

Posted

技术标签:

【中文标题】使用 Typescript 使用 getInitialProps 将 prop 传递到 Next.js 中的每个页面【英文标题】:Pass prop to every single page in Next.js with getInitialProps using Typescript 【发布时间】:2020-04-27 11:02:35 【问题描述】:

我有一个案例,我需要在页面呈现服务器端并使用 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”,这样我就可以通过获取令牌将“userAuthenticated”属性传递给每个页面,并确定它是否过期并且基于该道具,我可以向用户显示正确的 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 包装每一页,以便将道具 userAuthenticated 传递给我拥有的全局布局,因为我无法用它包装“_app.tsx”类组件,它总是给我一个错误...

这行得通...

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

但这不是...

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

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

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

有什么帮助吗?

【问题讨论】:

【参考方案1】:

对于那些可能遇到同样问题的人,我能够通过以下方式解决这个问题......

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 类型,但由于某种原因在这种情况下不起作用。

通过该解决方案,我能够了解用户是否经过身份验证,并将道具传递给全局布局,以便我可以在每个页面中使用它,而不必在每个页面中都这样做,而且如果您不希望每次都渲染页眉和页脚,如果它们必须依赖于 userAuthenticated 属性,则可以从中受益,因为现在您只需将页眉和页脚放在 GlobalLayout 组件中即可有userAuthenticated 道具供您使用:D

【讨论】:

我的评论与您的​​帖子和您的回答无关。我想说,ReactJS 遵循函数式编程或 OOP,但我在您的代码中看到了后端开发的思维方式与 OOP 思想。从另一个组件继承一个新类! ?我以前没见过。 @AmerllicA 我明白你在说什么,我什么都试过了。问题在于 s-s-r,如果我正在使用“创建 React 应用程序”并进行“客户端渲染”,我根本不会这样做,但我无法让它以任何其他方式与 s-s-r 一起工作,它甚至是 Next.js 推荐的方式。

以上是关于使用 Typescript 使用 getInitialProps 将 prop 传递到 Next.js 中的每个页面的主要内容,如果未能解决你的问题,请参考以下文章

java JDBC 的使用总结

PAT甲级1077 Kuchiguse (20 分)(cin.ignore()吃掉输入n以后的回车接着用getine(cin,s[i])输入N行字符串)

typescript使用 TypeScript 开发 Vue 组件

使用来自使用旧版 Typescript 确定类型的 Typescript 定义文件

使用TypeScript中的TypeScript库

typescript 使用传递Type的Typescript继承