如何修复错误:水合失败,因为初始 UI 与我的代码中服务器上呈现的内容不匹配,如下所示

Har*_*ary 7 javascript reactjs next.js

export default function Page({ data1 }) {
  const [bookmark, setBookmark] = useState(
    typeof window !== 'undefined'
      ? JSON.parse(localStorage.getItem('bookmark'))
      : []
  );

  const addToBookmark = (ayatLs) => {
    setBookmark([...bookmark, ayatLs]);
  };

  useEffect(() => {
    localStorage.setItem('bookmark', JSON.stringify(bookmark));
  }, [bookmark]);

  return (
    <>
      <div className="modal-body">
        <ul>
          {bookmark.map((x) => (
            <li key={x.nomor}>{x.tr}</li>
          ))}
        </ul>
      </div>
    </>
  );
}
Run Code Online (Sandbox Code Playgroud)
    {data1.map((x)) => (
      <div className="pb-6 flex justify-between">
        <span
          onClick={() => addToBookmark(x)}
          className={`text-sm `}>
          {x.tr}
        </span>       
      </div>  
    )}
Run Code Online (Sandbox Code Playgroud)

当我typeof window !== 'undefined'使用Error: Hydration failed because the initial UI does not match what was rendered on the server in my code like this.

当我更改为localStorage.getItem('bookmark')错误时localStorage is not defined

当我单击 addToBookmark 时,它将数据从 props data1 存储到 localStorage,这里没有问题,但是当我在 localStorage 中较早获取数据并且我想映射出现错误的数据时

我的代码有什么问题,为什么我无法从 localStorage 映射数据,请帮我解决这个问题。

iva*_*ias 11

问题在于 Next.js 默认情况下预渲染页面,这意味着它们首先在服务器上渲染,然后发送到客户端进行水化

当您根据以下条件设置状态的默认值时,会出现错误:typeof window !== 'undefined'

考虑以下示例:

const Page = () => {
  const [name, setName] = useState(typeof window !== 'undefined' ? 'Peter' : 'Rick')
  
  return <h1>{name}</h1>
}

export default Page
Run Code Online (Sandbox Code Playgroud)

此代码将引发以下类型的错误Error: Text content does not match server-rendered HTML.

如果您检查页面的源代码,您将看到服务器上呈现的名称是“Rick”,而在客户端的第一个呈现上呈现的名称是“Peter”。服务器端渲染的内容和客户端首次渲染的内容之间必须匹配。

您可以做的是将localStorage数据收集和解析逻辑移至另一个useEffect,并在那里设置状态。这解决了问题,因为useEffect仅在客户端运行,因此您将有效地匹配服务器端和客户端的首次渲染内容。

export default function Page({ data1 }) {
  const [bookmark, setBookmark] = useState([])

  const addToBookmark = (ayatLs) => {
    setBookmark([...bookmark, ayatLs])
  }

  useEffect(() => {
    if (bookmark.length === 0) return
    localStorage.setItem('bookmark', JSON.stringify(bookmark))
  }, [bookmark])

  useEffect(() => {
    const bookmarkFromLocalStorage = localStorage.getItem('bookmark')

    const parsedBookmark =
      bookmarkFromLocalStorage !== null
      ? JSON.parse(bookmarkFromLocalStorage)
      : []

    setBookmark(parsedBookmark)
  }, [])

  return (
    <>
      <div className='modal-body'>
        <ul>
          {bookmark.map((x) => (
            <li key={x.nomor}>{x.tr}</li>
          ))}
        </ul>
      </div>
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)