添加'use client'后仍遇ReferenceError: localStorage未定义问题求助
问题解决:Next.js 13.4.1 中 localStorage is not defined 报错
错误原因
你已经给组件加了'use client',但Next.js App Router的客户端组件在服务端渲染阶段依然会执行组件的初始化代码(包括useState的初始值计算、变量声明)。而localStorage是浏览器专属的API,服务端环境中不存在,所以直接在组件顶层或useState初始值里调用localStorage.getItem会触发报错。
修复方案
有两种可靠的修复方式,核心都是确保localStorage的访问只在客户端环境执行:
方式1:使用useState的函数式初始值
利用函数式初始值只会在客户端执行的特性,先判断环境再读取本地存储:
'use client'; import { useState } from 'react'; const Cookies = () => { // 用函数式初始值,确保仅在客户端访问localStorage const [cookiesAccepted, setCookiesStatus] = useState<boolean>(() => { if (typeof localStorage !== 'undefined') { return !!localStorage.getItem('cookies-accepted'); } return false; }); 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;
方式2:使用useEffect延迟读取本地存储
useEffect的回调函数只会在客户端组件挂载后执行,服务端不会运行这部分代码:
'use client'; import { useState, useEffect } from 'react'; const Cookies = () => { const [cookiesAccepted, setCookiesStatus] = useState<boolean>(false); useEffect(() => { // 组件挂载后再读取localStorage const storedCookies = localStorage.getItem('cookies-accepted'); setCookiesStatus(!!storedCookies); }, []); 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;
补充说明
把组件移到app文件夹外没用,因为App Router中,只要组件被导入到服务端渲染的页面(比如app/page.tsx默认是服务端组件),客户端组件的初始化代码依然会在服务端被执行一次,直到完成hydration后才完全在客户端运行。所以关键是避免在服务端执行的代码块中访问浏览器专属API。
内容的提问来源于stack exchange,提问作者Big_Trouble
相关产品推荐
相关产品推荐

