You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13.4.12服务端页面使用Zustand本地存储的问题

解决方案

核心问题拆解

  1. 服务端环境没有localStorage,直接使用带persist的Zustand Store会报错
  2. Next.js 13服务端组件不能调用任何React Hooks(包括Zustand的useStore),所以你之前在服务端页面里调用useServerStore的写法本身就不合法

步骤1:改造Zustand Store适配服务端环境

修改Store代码,通过判断环境切换存储方式,服务端使用空存储避免报错:

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

// 判断是否处于浏览器环境
const isBrowser = typeof window !== "undefined";

const useProjectStore = create(
  persist(
    (set) => ({
      selectedProject: null,
      setSelectedProject: (project) => set({ selectedProject: project }),
    }),
    {
      name: "food-storage",
      // 服务端用空存储,浏览器用localStorage
      storage: createJSONStorage(() => isBrowser ? localStorage : {
        getItem: () => null,
        setItem: () => {},
        removeItem: () => {},
      }),
    }
  )
);

export default useProjectStore;

步骤2:服务端页面获取选中项目ID的正确方式

服务端无法直接读取客户端的localStorage,必须通过客户端主动传递状态的方式获取projectId,推荐两种方案:

方案一:URL搜索参数(推荐)

  1. 客户端组件更新URL参数:
    当用户选择项目时,同步更新URL的projectId参数:
"use client";
import useProjectStore from "@/stores/currentProject-store";
import { useRouter, useSearchParams } from "next/navigation";

const ProjectSelector = () => {
  const { selectedProject, setSelectedProject } = useProjectStore();
  const router = useRouter();
  const searchParams = useSearchParams();

  const handleSelectProject = (project) => {
    setSelectedProject(project);
    // 更新URL的query参数
    const newParams = new URLSearchParams(searchParams);
    newParams.set("projectId", project.id);
    router.push(`/dashboard/images?${newParams.toString()}`);
  };

  // 渲染项目选择UI...
};

export default ProjectSelector;
  1. 服务端页面读取参数并查询:
import { prisma } from "@/lib/prismaDb";
import ImageTable from "@/app/dashboard/components/imageTable";

// 服务端组件接收searchParams参数
const ImagesPage = async ({ searchParams }) => {
  const projectId = searchParams?.projectId;
  
  // 根据projectId查询数据,无ID则返回空列表
  const imageList = projectId ? await prisma.facebookImage.findMany({
    where: { projectId },
  }) : [];

  return (
    <div>
      <ImageTable imageList={imageList} />
    </div>
  );
};

export default ImagesPage;

方案二:Cookie存储

  1. 客户端组件写入Cookie:
"use client";
import useProjectStore from "@/stores/currentProject-store";

const ProjectSelector = () => {
  const { selectedProject, setSelectedProject } = useProjectStore();

  const handleSelectProject = (project) => {
    setSelectedProject(project);
    // 写入Cookie(有效期1天)
    document.cookie = `selectedProjectId=${project.id}; path=/; max-age=86400`;
  };

  // 渲染项目选择UI...
};

export default ProjectSelector;
  1. 服务端页面读取Cookie:
import { prisma } from "@/lib/prismaDb";
import ImageTable from "@/app/dashboard/components/imageTable";
import { cookies } from "next/headers";

const ImagesPage = async () => {
  const cookieStore = cookies();
  const projectId = cookieStore.get("selectedProjectId")?.value;
  
  const imageList = projectId ? await prisma.facebookImage.findMany({
    where: { projectId },
  }) : [];

  return (
    <div>
      <ImageTable imageList={imageList} />
    </div>
  );
};

export default ImagesPage;

关键注意事项

  • 服务端组件绝对不能调用任何React Hooks,所有数据必须通过URL参数、Cookie、请求头等方式传递
  • 改造后的Store在服务端初始化时不会依赖localStorage,避免报错

内容的提问来源于stack exchange,提问作者Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:05:15