NextJS 14服务器操作中使用localStorage等客户端对象的解决方案
NextJS 14 表单提交后存储JWT到localStorage的解决方案
Server Actions运行在服务端上下文,无法直接访问浏览器的localStorage,核心解决思路是让服务端返回JWT令牌,由客户端代码完成存储,以下是两种落地方案:
方案一:Server Action 返回令牌 + 客户端状态处理
这是NextJS 14推荐的写法,利用useFormState钩子接收Server Action的返回值,再在客户端执行存储操作。
步骤1:定义Server Action
可以单独抽离到文件中,也可以直接写在组件内(注意标记'use server'):
// app/actions.js 'use server' export async function createUser(formData) { const email = formData.get('email'); // 替换为实际的用户创建逻辑:比如调用数据库、验证邮箱、生成JWT const jwtToken = 'your-generated-jwt-token'; // 返回结果给客户端 return { success: true, token: jwtToken }; }
步骤2:客户端组件处理状态与存储
'use client' import { useFormState, useEffect } from 'react'; import { createUser } from './actions'; export default function RegisterForm() { // 初始化表单状态,第二个参数为初始值 const [formState, formAction] = useFormState(createUser, { success: false, token: '' }); // 监听状态变化,拿到令牌后存入localStorage useEffect(() => { if (formState.success && formState.token) { localStorage.setItem('jwt', formState.token); // 可选:注册成功后跳转页面 // window.location.href = '/dashboard'; } }, [formState]); return ( <form className="border p-6 rounded-md" action={formAction}> <label htmlFor="email" className="block mt-2 text-xs font-semibold text-gray-600 uppercase">E-mail</label> <input id="email" type="email" name="email" placeholder="Enter your e-mail" autoComplete="email" required className="block w-full p-2 mt-2 text-gray-800 transition-all border-b border-gray-200 focus:text-gray-500 outline-none focus:border-gray-400" /> <button type="submit" className="mt-4 px-4 py-2 bg-blue-500 text-white rounded">注册</button> {formState.success && <p className="mt-2 text-green-500">注册成功!</p>} </form> ); }
方案二:传统API路由 + 客户端Fetch提交
如果更习惯手动控制请求流程,可以用API路由配合客户端fetch,完全由客户端处理响应和存储:
步骤1:创建API路由
// app/api/signup/route.js export async function POST(request) { const formData = await request.formData(); const email = formData.get('email'); // 执行用户创建逻辑,生成JWT令牌 const jwtToken = 'your-generated-jwt-token'; return Response.json({ success: true, token: jwtToken }); }
步骤2:客户端组件编写提交函数
'use client' export default function RegisterForm() { async function handleSubmit(e) { e.preventDefault(); const formData = new FormData(e.target); try { const res = await fetch('/api/signup', { method: 'POST', body: formData, }); const data = await res.json(); if (data.success && data.token) { localStorage.setItem('jwt', data.token); // 注册成功后跳转页面 window.location.href = '/'; } } catch (error) { console.error('注册失败:', error); } } return ( <form className="border p-6 rounded-md" onSubmit={handleSubmit} > <label htmlFor="email" className="block mt-2 text-xs font-semibold text-gray-600 uppercase">E-mail</label> <input id="email" type="email" name="email" placeholder="Enter your e-mail" autoComplete="email" required className="block w-full p-2 mt-2 text-gray-800 transition-all border-b border-gray-200 focus:text-gray-500 outline-none focus:border-gray-400" /> <button type="submit" className="mt-4 px-4 py-2 bg-blue-500 text-white rounded">注册</button> </form> ); }
关于你之前的报错说明
你之前尝试混用Server Action和客户端fetch导致报错,原因是:
- Server Action必须标记
'use server'且运行在服务端上下文,不能直接在客户端fetch中调用; - 若使用API路由,无需依赖Server Action,直接通过客户端fetch请求即可,不需要
'use server'指令。
内容的提问来源于stack exchange,提问作者Krushnal Patel
相关产品推荐
相关产品推荐

