NextJS Zustand 持久状态

Sal*_*zal 7 reactjs next.js zustand

我有这个 github 仓库:https ://github.com/salmanfazal01/next-firebase-starter

基本上我正在使用 NextJS、Firebase 和 Zustand 创建一个基本样板作为全局状态管理器

我无法在 Zustand 中保留主题、用户等一些状态。刷新窗口后,它默认返回默认主题。

我确实尝试了 zustand 提供的持久中间件,尽管它有效,但它会导致服务器和客户端的内容不匹配错误

Error: Hydration failed because the initial UI does not match what was rendered on the server.
Run Code Online (Sandbox Code Playgroud)

我将如何在不使用其他库(如下一个主题)的情况下将主题模式保留在状态中?

TD1*_*TD1 6

欲望:

  • 您希望在使用服务器端渲染 (SSR) 和 next.js 时在不同页面之间移动时保持状态。例如,您希望在页面之间移动时显示用户的个人资料图片。

初步实施:

  • 您将数据保存到用户浏览器的本地存储中。您正在使用 Zustand 来执行此操作。

问题:

  • 您看到一条错误,指出客户端内容与服务器呈现的 html 不匹配。next.js 的示例:
# Unhandled Runtime Error

Error: Text content does not match server-rendered HTML. See more info here: [https://nextjs.org/docs/messages/react-hydration-error](https://nextjs.org/docs/messages/react-hydration-error)
Run Code Online (Sandbox Code Playgroud)
  • 原因是您正在使用特定变量(例如,user_name)在服务器上渲染 html。然后,当您在客户端加载页面并user_name 从客户端本地存储加载变量时,该变量包含与服务器端不同的值。这会导致不匹配,错误消息会突出显示这一点(https://nextjs.org/docs/messages/react-Hydration-error)。

解决方案:

  • 当客户端 html 不同时,在组件/页面首次渲染 html 后从本地存储加载变量。要使用 next.js 解决此问题,请仅在钩子中从本地存储加载数据useEffect (相当于onMountedNuxt/Vue)。
  • 以 Jotai 为例:
// store.js
import { atomWithStorage } from 'jotai/utils'  
  
export const countAtom = atomWithStorage('countAtom', 0);  
Run Code Online (Sandbox Code Playgroud)
# Unhandled Runtime Error

Error: Text content does not match server-rendered HTML. See more info here: [https://nextjs.org/docs/messages/react-hydration-error](https://nextjs.org/docs/messages/react-hydration-error)
Run Code Online (Sandbox Code Playgroud)
// store.js
import { atomWithStorage } from 'jotai/utils'  
  
export const countAtom = atomWithStorage('countAtom', 0);  
Run Code Online (Sandbox Code Playgroud)


Rag*_*tal 6

在 Next.js 13.4 中,您只需添加本文中提到的代码即可。我已链接到文章:ZUSTAND ARTICLE

商店.ts:

"use client";
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";

export const useBearStore = create(
  persist(
    (set: any, get: any) => ({
      bears: 0,
      addABear: () => set({ bears: get().bears + 1 }),
    }),
    {
      name: "food-storage", // name of the item in the storage (must be unique)
      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
      skipHydration: true,
    }
  )
);
Run Code Online (Sandbox Code Playgroud)

页面.tsx:

"use client";

import { useBearStore } from "@/store";
import React, { useState, useEffect } from "react";

const page = () => {
  //TODO: MUST BE ADD THIS CODE TO REMOVE HYDRATION ERROR::
  useEffect(() => {
    useBearStore.persist.rehydrate();
  }, []);
  function Controls() {
    const increasePopulation = useBearStore((state: any) => state.addABear);
    return (
      <button
        className="py-4 px-2 rounded-md bg-blue-400"
        onClick={increasePopulation}
      >
        one up
      </button>
    );
  }
  const DisplayCounter = () => {
    const counter = useBearStore((state: any) => state.bears);
    return <div>Counter: {counter}</div>;
  };
  return (
    <div>
      <h1>Hello Do you see me</h1>
      {Controls()}
      {DisplayCounter()}
    </div>
  );
};

export default page;
Run Code Online (Sandbox Code Playgroud)


小智 0

我想您需要使用 useEffect() 将 Zustand 状态转移到 useState() 以防止水合错误。

您在任何页面/组件中导入的 Zustand 状态数据需要在 useEffect() 内更新为新的 useState()。

https://nextjs.org/docs/messages/react-Hydration-error