background

I was trying to write the Renderer of the article table by myself yesterday. After a certain save, suddenly the following error appeared:

Hydration failed because the initial UI does not match what was rendered on the server.

Find problems

official documentation

First, check the official description of the problem according to the error log:

Title Image

react-hydration-error | Next.js

Officials blamed the problem on two possible reasons:

  1. In the rendering process, the server side and the browser side are judged, resulting in different rendering results. Common questions are:
    1. Judging whether the window exists outside the React Hook and affecting the return result
    2. The possible reasons that are not officially mentioned are: environment variables available only on the server side are used during the rendering process, such as process.env
  2. The css-in-js library is used, but the corresponding plug-ins are not set, such as swc and Babel that come with react. For example use:
    1. Styled Components
    2. Emotion

Obviously, my case is not among them: I am triggering this error when the environment has not changed, the code has changed.

Framework bug

So it is suspected that the bug of Next.JS and React 18 is triggered. After some searching, I found that there is indeed a related GitHub Issue, but the trigger condition is to embed < in the <p> element div> - which doesn't work in my case either. After that, I also tried to close swcMinify of Next.JS and upgrade to Canary, but the problem is still not solved.

other possibilities

  • CDN server: CDN servers such as Cloudflare will cache some static scripts and json, which will also cause inconsistent rendering between the server and the client. You can try clearing the CDN cache
  • The <title> component has multiple children: for example, <span>Hello {world} foo</span> should not be used, Instead, use <span>{`Hello ${world} foo`}</span>. See GitHub Discussion: https://github.com/vercel/next.js/discussions/38256.

code error

In the end, you can only go back to the error itself and turn your attention back to the code.

After trying to annotate the components one by one, I located the problematic component and its code. One of them is:

...
    const {withHeadings, content} = props.data;
    if (withHeadings) {
        let headingData = content.shift();
        heading = (<thead>
            <tr>
                {
                    headingData.map((cell, index) => {
                        return <th key={index}>{cell}</th>
                    })
                }
            </tr>
            </thead>
        );
    }
    ...

Those who may have experience have seen the problem:

Since JS does not clearly distinguish between pass-by-value or pass-by-reference (shared call) when passing parameters, I made a mistake here: by destructuring the declaration, I changed it from props.data got content, and called content.shift( )Original parameter changed.

Here, although the new array content is taken out through destructuring declaration, it is still the original Shared calls for arrays in -code">props objects. It was modifying the original incoming parameters that caused my problem.

Let's review the rendering process of Next.js first:

  1. Next.js first pre-renders on the server (Server Side Generation) and generates static HTML and JSON.
  2. The JSON will then be passed to the browser and rendered again (ie "Hydration").

In step 1, the content array is changed by content.shift() in the rendering process and becomes a new slice< /b> (content.slice[1, content.length]), the JSON generated subsequently is a slice of content; when performing the steps 2. When the browser uses JSON for Hydration, it is not the original content. Therefore, after comparing the rendering results, the framework will naturally find that one less line of content is rendered, and throw the Hydration failed error.

fix the problem

In summary, the correct code should copy the original array and operate on the new array:

...
    const {withHeadings} = props.data;
    if (withHeadings) {
        const content = [...data.content]; //复制数组的内容
        let headingData = content.shift();
        ...
    }
    ...

Summarize

When writing render functions, care should be taken not to modify props - not only the props itself, but also the references he holds in the properties .

 Next.JS: The React Framework for Production (Source: https://nextjs.org/static/twitter-cards/home.jpg)
Next.JS: The React Framework for Production (Source: https://nextjs.org/static/twitter-cards/home.jpg)

THIS SERVICE MAY CONTAIN TRANSLATIONS POWERED BY GOOGLE. GOOGLE DISCLAIMS ALL WARRANTIES RELATED TO THE TRANSLATIONS, EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF ACCURACY, RELIABILITY, AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

Comments

Create Comment

Your email is not known to other users and is only used to generate unique random nicknames.