简体   繁体   中英

How does Next JS server side rendering works when result hits client?

I am having a problem in NextJS app.

In the _app.js file I instantiate a Context (which in turns instantiates a State )

In my page ContextTest I declare a getServerSideProps() .

This returns some data from an API, that is passed to the _app.js and saved into Context's state .

So far, I have the data that I need.

Then it goes to the target page component, the component reads the context's state and sets a variable. The component successfully outputs my variable, which is in the form

let userName = userState.username ? userState.username : 'Guest' 

When page loads, I inspect the HTML from the request and indeed I have userState.username value.

But then the shown HTML in the browser has Guest .

There are two things to note:

1) The server side renders as expected 2) The App is executed again in the client for that page (so why did I render the app server side then?). The result is that this page doesn't have the result of getServerSideProps() so my state is empty and I get a warning in console. 3) The HTML displayed in the browser is the one resulting of client execution of React app. (Why?)

__app.js

import React, {useContext} from "react";
import {UserContext} from "../lib/Context/UserContext"

export default function RankerApp({Component, pageProps}) {
    const [userState, setUserState] = useContext(UserContext)();
    const [listState, setListState] = useContext(ListContext)();

    if (pageProps.serverContext && pageProps.serverContext.user) {
        setUserState({...userState, ...pageProps.serverContext.user})
    }
    delete pageProps['serverContext'];

    return (
        <ListContext.Provider value={[listState, setListState]}>
            <UserContext.Provider value={[userState, setUserState]}>
                <Component {...pageProps} />
            </UserContext.Provider>
        </ListContext.Provider>
    );
}

pages/contextTest.js

import React, {useContext} from 'react';
import {UserContext} from "../lib/Context/UserContext";
import Link from "next/link";
import UserContextSeeder from "../lib/User/UserContextSeeder";

export default function ContextPage1(props) {

    let [userState, setUserState] = useContext(UserContext);

    const currentLoggedInUserData = userState.loggedInUserId ? userState.users[userState.loggedInUserId] : null;
    const currentLoggedInUserName = currentLoggedInUserData ? currentLoggedInUserData.userName : 'Guest';

    return (
        <div>
            <h1>Page 1</h1>
            <Link href="contextTest2">
                <a>Page 2</a>
            </Link>
            <h1>Current Logged In User: {currentLoggedInUserName}</h1>
        </div>
    );
}

export async function getServerSideProps(context) {
    let userContext = {};
    // await UserContextSeeder(context).then(context => {userContext = context}); commented for simplicty
    userContext = {loggedInUserId: 222, users: {222: {userName: "Jorge"}}; // suppose this is the result 
    return {
        props: {
            serverContext: {
                user: {...userContext}
            }
        },
    }
}

More context: https://github.com/zeit/next.js/discussions/11871

1个

Let's consider a world where NextJS did not hydrate. You have build the DOM from the server and sent it to the client, client showed the HTML, everyone is happy. Now React has to execute a useEffect which will trigger some change in the DOM, but if React does not have the DOM, how would it look do the comparison the virtual DOM process?

So, we need a hydration/render process on the client even if we have already done it on the server. Server side rendering is not a process by which we spare the browser some jobs, it is how we make sure content is ready early for the user and bots to see.

Josh Comeau has a nice explanation on his blog .

On a different note, let's look at this code:

if (pageProps.serverContext && pageProps.serverContext.user) {
   setUserState({...userState, ...pageProps.serverContext.user})
}
delete pageProps['serverContext'];

return ();

This here is called a side effect and one which is being executed in render . While this works with normal React, it is prohibited in StrictMode and will break with Suspense (it can also break in normal cases). So we move this to a useEffect .

useEffect(() => {
  if (pageProps.serverContext && pageProps.serverContext.user) {
   setUserState(prevUserState => ({...prevUserState, ...pageProps.serverContext.user}));
  }
}, [])

Note that I'm not deleting the page prop, because we don't need to. useEffect is only executed on the client, so the effects of this are going to show up only on the client. If you want them to show up on the first render, you would have to use the prop to render username. Example:

const [userState, setUserState] = useContext(UserContext)(props.serverContext ? props.serverContext.user : null);

In context hook:

const initialState = {
    loggedInUserId: undefined,
    users: {}
}

const userContextUpdater = (initialUser) => {
    let [state, setState] = useState({
      ...initialState,
      users: initialUser || initialStateusers,
    })
    return [state, setState];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM