admin管理员组

文章数量:1327849

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 ponent, the ponent reads the context's state and sets a variable. The ponent 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}); mented for simplicty
    userContext = {loggedInUserId: 222, users: {222: {userName: "Jorge"}}; // suppose this is the result 
    return {
        props: {
            serverContext: {
                user: {...userContext}
            }
        },
    }
}

More context: .js/discussions/11871

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 ponent, the ponent reads the context's state and sets a variable. The ponent 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}); mented for simplicty
    userContext = {loggedInUserId: 222, users: {222: {userName: "Jorge"}}; // suppose this is the result 
    return {
        props: {
            serverContext: {
                user: {...userContext}
            }
        },
    }
}

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

Share asked Apr 14, 2020 at 21:15 JorgeeFGJorgeeFG 6,00113 gold badges64 silver badges100 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

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 parison 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];
}

本文标签: javascriptHow does Next JS server side rendering works when result hits clientStack Overflow