使用 NextJS 用 Class Components 和 getInitialProps 做 s-s-r,render 方法有未定义的数据

Posted

技术标签:

【中文标题】使用 NextJS 用 Class Components 和 getInitialProps 做 s-s-r,render 方法有未定义的数据【英文标题】:Using NextJS to do s-s-r with Class Components and getInitialProps, render method has undefined data 【发布时间】:2020-06-05 03:31:35 【问题描述】:

我对 javascript/React/NextJS 还很陌生。我已经编写了一个 React 应用程序,现在我正在尝试对其进行转换,以便将其全部呈现在服务器上。

我浏览了https://nextjs.org/learn/basics/getting-started 上的教程,然后我尝试将这些知识应用到我的应用程序中。

我的应用中有以下要求

我有一个主细节场景。主页呈现文章列表,单击其中一篇文章会将用户带到文章详细信息。 我想要干净的网址,例如“/article/123”而不是“article?id=123”。 我正在使用 3rd 方库从服务器获取数据,因此我必须使用 Promise 来获取数据 我需要使用类组件而不是功能组件(由于其他原因无法移动到功能组件)。 我希望所有的渲染都发生在服务器而不是客户端上

我不清楚如何在“getInitialProps”中调用我的 3rd 方库中的异步方法并返回数据。我尝试的任何方法都不起作用,谷歌搜索也没有帮助。

我的项目结构是

- components
     |- header.js
     |- footer.js
     |- layout.js
- pages
     |- index.js
     |- article
          |- [id].js
- scripts
     |- utils.js

index.js 只为 2 篇文章呈现 2 个链接

export default function Index() 
     return (
        <div>
            <Link href="/article/[id]" as=`/article/1`>
                <a>Article 1</a>
            </Link>
            <Link href="/article/[id]" as=`/article/2`>
                <a>Article 2</a>
            </Link>
        </div>
    );
 

utils.js 有一个 util 方法来调用 3rd 方库方法

export function getDataFromThirdPartyLib(articleId)  
    return thirdPartyLib.getMyData(
        "articleId" : articleId,
    ).then(function (item)   
        return item;
   );
 

article/[id].js 有

 function getData(articleId)
     console.log("getData")
     getDataFromThirdPartyLib(articleId)
        .then((item) => 
            console.log("got data")
            return 
                item: item
            
        );
  

 class ArticleDetails extends React.Component 

     static async getInitialProps(ctx) 
         const data = await getData(articleId);
         // I also tried the following
         // const data = getData(articleId);

         return
             data : data 
         
     

     render() 
         console.log("IN RENDER")
         console.log("this.props.data") // THIS IS UNDEFINED
         // my html
     
  

问题

控制台日志显示数据在 render() 中始终未定义。我可以从获取数据和获取数据中看到我的日志记录,但是这些是在我的渲染日志记录之后渲染的,因此一旦获取数据就不会刷新渲染。

不同的尝试 1

我尝试在我的组件定义中使用“getData”(与它相对),但这总是失败,因为它说“getData”不是一个函数。

 class ArticleDetails extends React.Component 

     static async getInitialProps(ctx) 
         const data = await getData(articleId); // FAILS AS SAYS getData is not a function

         return
             data : data 
         
     

     getData(articleId)
         console.log("getData")
         getDataFromThirdPartyLib(articleId)
            .then((item) => 
                return 
                    item: item
                
            );
      

     render() 
         console.log("IN RENDER")
         console.log(this.props.data) // THIS IS UNDEFINED
         // my HTML
     
  

不同的尝试 2

我尝试将“getData”中的逻辑直接放在“getInitialProps”中,但这也不起作用,因为它说 getInitialProps 必须返回一个对象。

 class ArticleDetails extends React.Component 

     static async getInitialProps(ctx) 
          getDataFromThirdPartyLib(articleId)
            .then((item) => 
                return 
                    data: data
                
          );
     

     render() 
         console.log("IN RENDER")
         console.log(this.props.data) // THIS IS UNDEFINED
         // my HTML
     
  

工作版本 - 但不是 s-s-r

我可以让它工作的唯一方法是像 React App 中的类组件一样编写它。但我相信“componentDidMount”不是在服务器上调用的,而是在客户端调用的。所以这挫败了我让一切都在服务器中呈现的尝试。

class ArticleDetails  extends React.Component 

    state = 
        item: null,


    componentDidMount() 
        this.getData();
    


    getData(articleId)
         console.log("getData")
         getDataFromThirdPartyLib(articleId)
            .then((item) => 
                self.setState(                
                    item: item
                
            );
     

    render() 
         console.log("IN RENDER")
         console.log(this.state.item) // THIS HAS A VALUE
         // my HTML
        


Steve Holgado 提供的解决方案

问题是我的“getData”方法没有像史蒂夫所说的那样返回承诺。

但是我的“getData”方法比我最初在这篇文章中提出的要复杂一些,这就是为什么当我第一次尝试史蒂夫的解决方案时它不起作用的原因。

我的 getData 嵌套了两个 Promise。如果我把回报放在两个承诺上,它就会完美地工作。

function getData(articleId)
     return getDataFromThirdPartyLib(articleId)
        .then((item) => 

             return getSecondBitOfDataFromThirdPartyLib(item)
                .then((extraInfo) => 
                    return 
                        item: item,
                        extraInfo : extraInfo
                    
                );

            return 
                item: item
            
        );
  

【问题讨论】:

【参考方案1】:

您需要在article/[id].js 中返回来自getData 的承诺:

function getData(articleId)
  // return promise here
  return getDataFromThirdPartyLib(articleId)
    .then((item) => 
      return 
        item: item
      
    );

...否则undefined 会隐式返回。

另外,你从哪里得到articleId

它应该来自url吗?

static async getInitialProps(ctx) 
  const articleId = ctx.query.id;
  const data = await getData(articleId);

  return 
    data: data 
  

希望这会有所帮助。

【讨论】:

是的,我正在从 URL 中获取 articleId,就像您在示例中添加的一样。对不起,我错过了这个。不幸的是,按照您的建议更改代码,我的渲染方法仍然“未定义” 如果您在.then 之后通过控制台登录getDataFromThirdPartyLib(articleId)@,您会看到正确的数据吗? 是的,我得到了数据。我唯一的奇迹是这个文件在“pages/p/[id].js”中。很多地方都说“getInitialProps”只适用于页面。这是否也包括这些子页面?我想我的 getData 被调用了 是的,这应该仍然有效。你有公开的 git 仓库吗? 不,我没有公共 git 存储库。但我在你的帮助下找到了问题。我的 getData 方法实际上是链接承诺。在我阅读您的评论后,我只在第一个上放了“return”。当我将两者都返回时,我得到了正确的数据。我更新了我的问题,因为它更容易看到解决方案。

以上是关于使用 NextJS 用 Class Components 和 getInitialProps 做 s-s-r,render 方法有未定义的数据的主要内容,如果未能解决你的问题,请参考以下文章

NextJS getServerSideProps 将数据传递给 Page Class

nextjs用静态类名添加动态类名[重复]

用 flex 填充页面并用 body 将页脚粘贴到底部(Tailwindcss,Nextjs)

用 Jest 测试 NextJS API 中间件

NextJS 和 Vercel:网站不呈现/使用 CSS 和 ReactJS

如何通过 vue-class-component 使用 Vue Router 延迟加载?