admin管理员组

文章数量:1426652

I am fairly new to JavaScript/React/NextJS. I have written a React app and I am now trying to convert it so that all of it renders on the server.

I went through the tutorials on and then i tried to apply that knowledge to my app.

I have the following requirements in my app

  • I have a master detail scenario. The main page renders a list of articles, clicking on one of those articles takes the user to articles details.
  • I want to have clean urls, such as "/article/123" and not "article?id=123".
  • I am using a 3rd party library to get the data from the server, therefore I have to use promises to get the data
  • I need to use Class Components not functional ponents (for other reasons can not move to functional ponents).
  • I want all the rendering to occur on the server not the client

I am unclear how in "getInitialProps" i would call my async method in my 3rd party library and return the data. Nothing i try is working and googling hasn't helped.

My project structure is

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

index.js just renders 2 links for 2 articles

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 has a util method to call the 3rd party library method

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

article/[id].js has

 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
     }
  }

Problem :

console logging shows that the data is always undefined in render(). I can see my logging from getting the data and data is obtained, but these render after my render logging, so render is not refreshed once the data is obtained.

Different attempt 1

I tried having "getData" inside my ponent definition (as apposed to outside it) but this always fails as it says the "getData" is not a function.

 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
     }
  }

Different attempt 2

I tried having the logic in "getData" directly in "getInitialProps" but that also doesn't work as it says getInitialProps must return an object.

 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
     }
  }

Working version - but not SSR

The only way i can get this to work is to write it like a class ponent in a React App. But I believe "ponentDidMount" is not called on the server, its called in the Client. So this defeats my attempt of getting everything to render in the server.

class ArticleDetails  extends React.Component {

    state = {
        item: null,


    ponentDidMount() {
        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
    }    

}

SOLUTION as provided by Steve Holgado

The problem was that my "getData" method was not returning the promise as Steve said.

However my "getData" method was a little more plex than I put in this post originally which was why when I first tried Steve's solution it did not work.

My getData is nesting two promises. If i put the return on BOTH promises it works perfectly.

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

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

            return {
                item: item
            }
        });
}  

I am fairly new to JavaScript/React/NextJS. I have written a React app and I am now trying to convert it so that all of it renders on the server.

I went through the tutorials on https://nextjs/learn/basics/getting-started and then i tried to apply that knowledge to my app.

I have the following requirements in my app

  • I have a master detail scenario. The main page renders a list of articles, clicking on one of those articles takes the user to articles details.
  • I want to have clean urls, such as "/article/123" and not "article?id=123".
  • I am using a 3rd party library to get the data from the server, therefore I have to use promises to get the data
  • I need to use Class Components not functional ponents (for other reasons can not move to functional ponents).
  • I want all the rendering to occur on the server not the client

I am unclear how in "getInitialProps" i would call my async method in my 3rd party library and return the data. Nothing i try is working and googling hasn't helped.

My project structure is

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

index.js just renders 2 links for 2 articles

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 has a util method to call the 3rd party library method

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

article/[id].js has

 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
     }
  }

Problem :

console logging shows that the data is always undefined in render(). I can see my logging from getting the data and data is obtained, but these render after my render logging, so render is not refreshed once the data is obtained.

Different attempt 1

I tried having "getData" inside my ponent definition (as apposed to outside it) but this always fails as it says the "getData" is not a function.

 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
     }
  }

Different attempt 2

I tried having the logic in "getData" directly in "getInitialProps" but that also doesn't work as it says getInitialProps must return an object.

 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
     }
  }

Working version - but not SSR

The only way i can get this to work is to write it like a class ponent in a React App. But I believe "ponentDidMount" is not called on the server, its called in the Client. So this defeats my attempt of getting everything to render in the server.

class ArticleDetails  extends React.Component {

    state = {
        item: null,


    ponentDidMount() {
        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
    }    

}

SOLUTION as provided by Steve Holgado

The problem was that my "getData" method was not returning the promise as Steve said.

However my "getData" method was a little more plex than I put in this post originally which was why when I first tried Steve's solution it did not work.

My getData is nesting two promises. If i put the return on BOTH promises it works perfectly.

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

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

            return {
                item: item
            }
        });
}  
Share Improve this question edited Feb 21, 2020 at 17:04 se22as asked Feb 20, 2020 at 18:11 se22asse22as 2,3825 gold badges33 silver badges59 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 1

You need to return the promise from getData in article/[id].js:

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

...otherwise undefined is returned implicitly.

Also, where are you getting articleId from?

Is it supposed to e from the url?

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

  return {
    data: data 
  }
}

Hope this helps.

本文标签: