即使添加了"use client",仍然出现ReferenceError: localStorage未定义的错误。

4
我有一个简单的app/components/organisms/Cookies.tsx模态窗口组件,我将其导入到app/page.tsx中。我在组件顶部添加了'use client'指令,但出现了以下错误:

ReferenceError: localStorage未定义

enter image description here

我的Cookies.tsx代码:

'use client';
import { useState } from 'react';

const Cookies = () => {
  const localStorageCookies = localStorage.getItem('cookies-accepted');
  const [cookiesAccepted, setCookiesStatus] = useState<boolean>(!!localStorageCookies);

  const acceptCookies = () => {
    localStorage.setItem('cookies-accepted', 'true');
    setCookiesStatus(true);
  }
  
  return (
    <>
      {!cookiesAccepted && (
        <section className="fixed max-w-md p-4 mx-auto bg-white border border-gray-200 left-12 bottom-16  rounded-2xl z-20">
          <h2 className="font-semibold text-gray-800 "> Cookie Notice</h2>

          <p className="mt-4 text-sm text-gray-600">
            We use cookies to ensure that we give you the best experience on our
            website.{' '}
            <a href="#" className="text-blue-500 hover:underline">
              Read cookies policies
            </a>
            .{' '}
          </p>

          <div className="flex items-center justify-between mt-4 gap-x-4 shrink-0">
            <button className="text-xs text-gray-800 underline transition-colors duration-300 hover:text-gray-600 focus:outline-none">
              Manage your preferences
            </button>

            <button
              className=" text-xs bg-gray-900 font-medium rounded-lg hover:bg-gray-700 text-white px-4 py-2.5 duration-300 transition-colors focus:outline-none"
              onClick={acceptCookies}
            >
              Accept
            </button>
          </div>
        </section>
      )}
    </>
  );
};

export default Cookies;

我做错了什么?我试图将 Cookies.tsx 放在 app 文件夹之外,但没有帮助。

我正在使用的是 Next.js 版本 13.4.1


2
请注意,使用 'use client' 指令的组件仍会得到服务器端渲染(这就是导致错误的原因)。任何访问 Web API(如 localStorage)的逻辑都需要在 useEffect 中完成。 - juliomalves
1
@juliomalves是正确的。如果您想要关于此主题的文档链接和代码的安全实现,请查看下面的答案。 - Youssouf Oumar
1个回答

9
正如您可以在渲染上阅读到的那样,实际上,服务器和客户端组件都首先在服务器上进行渲染,并作为HTML发送到浏览器(您可以在浏览器的网络选项卡中查看生成的HTML文件)。以下是文档中的引用
“为了优化初始页面加载,Next.js将使用React的API在服务器上为客户端和服务器组件渲染静态HTML预览。这意味着,当用户首次访问您的应用程序时,他们将立即看到页面内容,而无需等待客户端下载、解析和执行客户端组件JavaScript包。”
两者之间的区别在于,当您添加"use client"时,这意味着该组件包含客户端交互功能:因此,Next.js应该发送所需的JavaScript(例如事件处理程序、效果等)并将其附加到以前显示为纯HTML的组件中。
所以任何与浏览器相关的东西,比如localStorage,不应该在客户端组件的主体中调用,而只能在useEffect、事件处理程序等内部调用。
这种方法在你的情况下应该有效:
"use client";

import { useEffect, useState } from "react";

const Cookies = () => {
  const [cookiesAccepted, setCookiesStatus] = useState<boolean | null>(null);

  const acceptCookies = () => {
    localStorage.setItem("cookies-accepted", "true");
    setCookiesStatus(true);
  };

  useEffect(() => {
    const localStorageCookies = localStorage.getItem("cookies-accepted");
    setCookiesStatus(!!localStorageCookies);
  }, []);

  if (cookiesAccepted === null || cookiesAccepted) {
    return null;
  }

  return (
    <section className="fixed max-w-md p-4 mx-auto bg-white border border-gray-200 left-12 bottom-16  rounded-2xl z-20">
      <h2 className="font-semibold text-gray-800 "> Cookie Notice</h2>

      <p className="mt-4 text-sm text-gray-600">
        We use cookies to ensure that we give you the best experience on our website.{" "}
        <a href="#" className="text-blue-500 hover:underline">
          Read cookies policies
        </a>
        .{" "}
      </p>

      <div className="flex items-center justify-between mt-4 gap-x-4 shrink-0">
        <button className="text-xs text-gray-800 underline transition-colors duration-300 hover:text-gray-600 focus:outline-none">
          Manage your preferences
        </button>

        <button
          className=" text-xs bg-gray-900 font-medium rounded-lg hover:bg-gray-700 text-white px-4 py-2.5 duration-300 transition-colors focus:outline-none"
          onClick={acceptCookies}
        >
          Accept
        </button>
      </div>
    </section>
  );
};

export default Cookies;

不要尝试使用window !== "undefined"的技巧,因为很可能会出现hydration error

这就是为什么他们应该称之为交互组件而不是客户端组件的原因。当我听到“客户端组件”这个词时,直觉上我会认为它们只应该存在于客户端,尽管现在我知道这并不正确。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接