Blog#250: Don't Blindly Trust Error Message Solutions: A Real-Life Example

250

Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.

1. Introduction

I've been working with Next.js for about three months in my job. During that time, I encountered an error during development: "Text content does not match server-rendered HTML." Although solving the issue wasn't difficult, the solution I used was different from what I found through Google or what ChatGPT suggested. In this article, I will introduce the general solutions and the one I used, showing that simply searching for error messages might not always lead you to the best solution. Even if you're not familiar with React, you should be able to understand the overall idea.

2. The Code that Caused the Error

Here is a simplified version of the code that caused the error. This component uses Server-Side Rendering (SSR) to render the content. The code is designed to display a comment as-is when it's within 30 characters, and truncate it with "..." if it exceeds 30 characters.

function MyComponent({comment}) {
  const displayedComment = comment.length > 30 
                            ? comment.slice(0, 30) + '...'
                            : comment
  return <div>{displayedComment}</div>
}

3. The Error and General Solutions

The error "Text content does not match server-rendered HTML" occurs when there's a discrepancy between server-side and client-side rendering. Let's look at some common solutions found through Google or provided by ChatGPT.

3.1. Avoid using browser-only libraries or application code

window is only undefined on the server-side, so using it can cause a discrepancy between server and client rendering. One solution is to use useEffect() which is called after rendering.

import { useEffect, useState } from 'react'
function MyComponent() {
  const [color, setColor] = useState('blue')
  useEffect(() => setColor('red'), [])
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

3.2. Use Dynamic Import for components you don't want to render server-side

If you don't want to render a component server-side, you can use the Dynamic Import feature.

import dynamic from 'next/dynamic'

const DynamicHeader = dynamic(() => import('../components/header'), {
  ssr: false,
})

3.3. Correct the HTML structure according to content model rules

HTML tags have parent-child relationship limitations called content models. For example, using a div tag as a child of a p tag is incorrect. You can solve this by replacing the p tag with a div tag.

export const CorrectComponent = () => {
  return (
    <div>
      <div>
        This is correct and should work because a div is really good for this
        task.
      </div>
      <Image src="/vercel.svg" alt="" width="30" height="30" />
    </div>
  )
}

3.4. Add an attribute to ignore the error

You can also use a JSX attribute to suppress the error, as shown in React's official documentation.

export default function App() {
  return (
    <h1 suppressHydrationWarning={true}>
      Current Date: {new Date().toLocaleDateString()}
    </h1>
  );
}

4. The Solution I Adopted

In my case, the error occurred when a comment was truncated right at an emoji or a specific kanji character. These characters are represented by a combination of two special Unicode code points called surrogate pairs. When one of these pairs was cut off by .slice(), the error occurred. While the general solutions mentioned earlier did eliminate the error, they didn't fix the character corruption issue. The best solution was to adjust the character count logic to account for surrogate pairs. I used an array conversion method to handle this.

function MyComponent({comment}) {
  const displayedComment = [...comment].length > 30 
                            ? [...comment].slice(0, 30).join('') + '...'
                            : comment
  return <div>{displayedComment}</div>
}

However, this method still does not solve a case like the one below. If you have a better solution, please let me know.

const str = '👨‍👩‍👧‍👦';
[...str].slice(0, 5);
// 結果: [ "👨", "‍", "👩", "‍", "👧" ]

Conclusion

This simple example demonstrates that the best solution for an error is not always the one found by searching the error message. Of course, it's essential to try the solutions you find, but understanding why the error occurs is also crucial. I hope this article helps you in your error-solving journey!

And Finally

As always, I hope you enjoyed this article and got something new. Thank you and see you in the next articles!

If you liked this article, please give me a like and subscribe to support me. Thank you. 😊

NGUYỄN ANH TUẤN

Xin chào, mình là Tuấn, một kỹ sư phần mềm đang làm việc tại Tokyo. Đây là blog cá nhân nơi mình chia sẻ kiến thức và kinh nghiệm trong quá trình phát triển bản thân. Hy vọng blog sẽ là nguồn cảm hứng và động lực cho các bạn. Hãy cùng mình học hỏi và trưởng thành mỗi ngày nhé!

Đăng nhận xét

Mới hơn Cũ hơn