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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:13:17