admin管理员组

文章数量:1325236

Since I like to add SSR to my uping 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 pleted)

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('');
  // 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 remended over getInitialProps. How can they be parable when they are not the same that getServerSideProps will on run in server?

Since I like to add SSR to my uping 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 pleted)

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./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 remended over getInitialProps. How can they be parable when they are not the same that getServerSideProps will on run in server?

Share Improve this question asked Mar 16, 2020 at 15:13 JerrycJerryc 3102 gold badges5 silver badges22 bronze badges 4
  • The result of getInitialProps is passed to the page ponent 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 Commented Mar 17, 2020 at 8:19
  • 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 Commented Mar 17, 2020 at 9:43
  • Were you able to resolve this? I am facing a similar issue. – Vishu Commented Aug 13, 2020 at 18:48
  • @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 munity will help u through. Good luck. – Jerryc Commented Aug 18, 2020 at 5:15
Add a ment  | 

2 Answers 2

Reset to default 7

Based on your ments, 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./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 ponent that could do all of this in a more consistent way. If you use this pattern a lot, I'd remend that.

I implemented a solution different than @Cully.

I did not want to use getInitialProps as it's considered legacy.

We can use this helper to know if we are on the first initial load, or after in a client navigation.

const isFirstServerCall = context.req?.url?.indexOf('/_next/data/') === -1;

So what I'm doing is :

Create a ssr.ts file like that :

import { DehydratedState } from '@tanstack/react-query';
import { GetServerSideProps } from 'next';

type ServerSideResponse = {
  dehydratedState?: DehydratedState;
};

export const onlyOnFirstServerCall = (
  getServerSidePropsFunc: GetServerSideProps<ServerSideResponse>
): GetServerSideProps<ServerSideResponse> => {
  return async (context: any) => {
    const isFirstServerCall = context.req?.url?.indexOf('/_next/data/') === -1;
    if (!isFirstServerCall) {
      return {
        props: {}
      };
    } else {
      return getServerSidePropsFunc(context);
    }
  };
};

And on your page, this function will be used like a HoC :

export const getServerSideProps = onlyOnFirstServerCall(async (ctx: any) => {
  const queryObject = ctx.query;
  ...what you do normally
});

So you have the choice to enable it or not.

本文标签: javascriptNextjs getInitialProps blocked the page rendering in client sideStack Overflow