Getting ReferenceError: localStorage is not defined even after adding “use client”

huangapple go评论79阅读模式
英文:

Getting ReferenceError: localStorage is not defined even after adding "use client"

问题

I have a simple app/components/organisms/Cookies.tsx modal window component that I import into app/page.tsx. I have added the 'use client' directive at the top of the component, but for some reason, I keep getting:
>ReferenceError: localStorage is not defined

Getting ReferenceError: localStorage is not defined even after adding “use client”

Code of my 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 ">&#127850; 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;

What am I doing wrong? I tried to put Cookies.tsx outside of app folder but it didn't help.

I'm using version Next.js version 13.4.1.

英文:

I have a simple app/components/organisms/Cookies.tsx modal window component that I import into app/page.tsx. I have added the &#39;use client&#39; directive at the top of the component, but for some reason, I keep getting:
>ReferenceError: localStorage is not defined

Getting ReferenceError: localStorage is not defined even after adding “use client”

Code of my Cookies.tsx:

&#39;use client&#39;;
import { useState } from &#39;react&#39;;

const Cookies = () =&gt; {
  const localStorageCookies = localStorage.getItem(&#39;cookies-accepted&#39;);
  const [cookiesAccepted, setCookiesStatus] = useState&lt;boolean&gt;(!!localStorageCookies);

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

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

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

            &lt;button
              className=&quot; 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&quot;
              onClick={acceptCookies}
            &gt;
              Accept
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/section&gt;
      )}
    &lt;/&gt;
  );
};

export default Cookies;

What am I doing wrong? I tried to put Cookies.tsx outside of app folder but it didn't help.

I'm using version Next.js version 13.4.1.

答案1

得分: 6

以下是您要翻译的内容:

根据渲染中所述,实际上,服务器和客户端组件都会首先在服务器上渲染,然后作为HTML发送到浏览器(您可以在浏览器的网络选项卡中查看生成的HTML文件)。以下是文档中的一句引用

为了优化初始页面加载,Next.js将使用React的API在服务器上为客户端和服务器组件呈现静态HTML预览。这意味着当用户首次访问您的应用程序时,他们将立即看到页面的内容,无需等待客户端下载、解析和执行客户端组件的JavaScript包。

两者之间的区别在于当您添加了&quot;use client&quot;时,这意味着此组件包含客户端交互:因此,Next.js应该发送所需的JavaScript(例如事件处理程序、效果等),并将其附加到先前以纯HTML显示的组件上。

因此,任何与浏览器特定的内容,例如localStorage,都不应在客户端组件中调用,而只能在useEffect、事件处理程序等内部调用。

在您的情况下,应该可以正常工作:

&quot;use client&quot;;

import { useEffect, useState } from &quot;react&quot;;

const Cookies = () =&gt; {
  const [cookiesAccepted, setCookiesStatus] = useState&lt;boolean | null&gt;(null);

  const acceptCookies = () =&gt; {
    localStorage.setItem(&quot;cookies-accepted&quot;, &quot;true&quot;);
    setCookiesStatus(true);
  };

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

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

  return (
    &lt;section className=&quot;fixed max-w-md p-4 mx-auto bg-white border border-gray-200 left-12 bottom-16  rounded-2xl z-20&quot;&gt;
      &lt;h2 className=&quot;font-semibold text-gray-800 &quot;&gt;&#127850; Cookie Notice&lt;/h2&gt;

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

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

        &lt;button
          className=&quot; 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&quot;
          onClick={acceptCookies}
        &gt;
          Accept
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/section&gt;
  );
};

export default Cookies;

请不要尝试window !== &quot;undefined&quot;的方法,因为很可能会出现水合错误

英文:

As you can read on rendering, actually, both Server and Client components render first on the server, and get sent as HTML to the browser (you can see the generated HTML file in the network tab of the browser). Here is a quote from the doc:

>To optimize the initial page load, Next.js will use React's APIs to render a static HTML preview on the server for both Client and Server Components. This means, when the user first visits your application, they will see the content of the page immediately, without having to wait for the client to download, parse, and execute the Client Component JavaScript bundle.

The difference between the two is that when you add &quot;use client&quot;, it means this component includes client interactivities: so Next.js should send the JavaScript needed (such as event handlers, effects, etc) and attach it to the previously displayed-as-plain-HTML component.

So anything that's browser specific, such as localStorage, should not be called in a client component body, but only inside useEffect, event handlers...

This should work in your case:

&quot;use client&quot;;

import { useEffect, useState } from &quot;react&quot;;

const Cookies = () =&gt; {
  const [cookiesAccepted, setCookiesStatus] = useState&lt;boolean | null&gt;(null);

  const acceptCookies = () =&gt; {
    localStorage.setItem(&quot;cookies-accepted&quot;, &quot;true&quot;);
    setCookiesStatus(true);
  };

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

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

  return (
    &lt;section className=&quot;fixed max-w-md p-4 mx-auto bg-white border border-gray-200 left-12 bottom-16  rounded-2xl z-20&quot;&gt;
      &lt;h2 className=&quot;font-semibold text-gray-800 &quot;&gt;&#127850; Cookie Notice&lt;/h2&gt;

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

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

        &lt;button
          className=&quot; 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&quot;
          onClick={acceptCookies}
        &gt;
          Accept
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/section&gt;
  );
};

export default Cookies;

And do not attempt the window !== &quot;undefined&quot; hack as most likely, you would get a hydration error.

huangapple
  • 本文由 发表于 2023年5月22日 00:18:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300847.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定