0
votes

Since I like to add SSR to my upcoming project to improve SEO, I would like to try out next. What I want is that only use SSR for the initial page, and the rest of navigations in the site will be client side rendering. I see the getInitialProps fit the most in this case, accordingly the documentations.

As my understanding, getInitialProps is run in server for the initial page rendering, and is run in the browser when navigating using next/link. The issue I found is that the getInitialProps seems to block the page rendering. (i.e. page changed/rendered after getInitialProps is completed)

import axios from 'axios'

function Posts(props) {
  return (
    <div>
      <div>Posts:</div>
      <div>
        {JSON.stringify(props)}
      </div>
    </div>
  )
}

Posts.getInitialProps = async (context) => {
  const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
  // Wait longer to see the effect
  // await (new Promise((resolve) => {
  //   setTimeout(resolve, 5000)
  // }))
  return {
    props: {
      posts: response.data
    }
  }
}

export default Posts;

How can I do it like in pure React, render the jsx first, then fill in the props? (the execution JSON.stringify(props) might be ignored at first)

Also, in next 9.3, the team introduced getServerSideProps, which is recommended over getInitialProps. How can they be comparable when they are not the same that getServerSideProps will on run in server?

1
The result of getInitialProps is passed to the page component as props. So next needs to wait for getInitialProps to resolve before rendering. Is that what you mean by "blocking"? If you want to do an initial render with dummy data or something, then just do normal react data fetching (e.g. useEffect).Cully
Yes, it is what i mean bu blocking. I cannot do a csr because i will need to ssr in case of this page being the initial page. Is my idea even valid?Jerryc
Were you able to resolve this? I am facing a similar issue.Vishu
@Vishu sorry for the late reply, I moved away from next. As I remember, it wasnt not What I expected. I am sure someone in the community will help u through. Good luck.Jerryc

1 Answers

4
votes

Based on your comments, you want to do the fetch on the server, on the initial page load. However, if navigating between pages you don't want rendering to block while waiting for getInitialProps to return.

One solution is to check if you're on the server, and do the fetch in getInitialProps. If on the client, don't do the fetch in getInitialProps and instead fetch using useEffect in your render method.

import {useEffect} from 'react'
import axios from 'axios'

const isServer = () => typeof window === 'undefined'

const getPosts = () => {
  return axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.data)
}

function Posts({posts}) {
  const [renderPosts, setRenderPosts] = useState(posts)

  useEffect(() => {
    if(posts === null) {
      getPosts()
        .then(setRenderPosts)
    }
  }, [])

  return (
    <div>
      <div>Posts:</div>
      <div>
        {JSON.stringify(renderPosts)}
      </div>
    </div>
  )
}

Posts.getInitialProps = async (context) => {
  if(isServer()) {
    return {
      posts: await getPosts(),
    }
  }
  else {
    return {
      posts: null,
    }
  }
}

export default Posts

By the way, you may be tempted to use getServerSideProps here, since it is only called if rendering on the server. However, when a page using getServerSideProps is rendered, it will actually make a call to the server to get data from getServerSideProps, even if you're navigating using next/link. From the Next.js 9.3 blog post:

When navigating between pages using next/link instead of executing getServerSideProps in the browser Next.js will do a fetch to the server which will return the result of calling getServerSideProps.

This would still cause the blocking issue you're wanting to avoid.

One final note, this might not be an idiomatic solution. There may be a more "standard" solution. I just wasn't able to find one. You could likely also use a wrapper around your page component that could do all of this in a more consistent way. If you use this pattern a lot, I'd recommend that.