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

Next.js 14中点击提交按钮后如何清空评论表单?

解决提交评论后清空表单的问题

问题分析

你当前的表单错误绑定了comments数组的属性(value={comments.username})——comments是评论列表数组,并非单个输入对象。而且页面属于服务器组件,无法使用客户端状态钩子(如useState)控制输入框内容,导致提交后无法自动清空。

解决方案:拆分客户端组件处理表单交互

把评论表单拆成独立的客户端组件,用客户端状态管理输入值,提交后直接重置状态。服务器组件仍负责数据获取和数据库请求,分工更清晰。

步骤1:创建客户端组件 app/components/CommentForm.jsx

'use client';

import { useState } from 'react';
import { handleComments } from '@/app/actions';

export default function CommentForm({ postId }) {
  const [username, setUsername] = useState('');
  const [comment, setComment] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    
    // 组装表单数据并调用服务器动作
    const formData = new FormData();
    formData.append('username', username);
    formData.append('comment', comment);
    formData.append('post_id', postId);
    
    await handleComments(formData);
    
    // 清空输入框
    setUsername('');
    setComment('');
  };

  return (
    <form onSubmit={handleSubmit} className="text-black flex">
      <input
        name="username"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
        className="mr-2 px-4 py-2 border border-gray-300 rounded-md"
      />
      <input
        name="comment"
        placeholder="Comment..."
        value={comment}
        onChange={(e) => setComment(e.target.value)}
        className="flex-1 mr-2 px-4 py-2 border border-gray-300 rounded-md"
      />
      <button
        type="submit"
        className="px-4 py-2 bg-blue-500 text-white font-bold rounded-md hover:bg-blue-600"
      >
        Submit
      </button>
    </form>
  );
}

步骤2:抽离服务器动作到 app/actions.js

把数据库操作逻辑单独抽离,方便页面和组件复用:

'use server';

import { sql } from '@vercel/postgres';
import { revalidatePath } from 'next/cache';

export async function handleComments(formData) {
  const comment = formData.get('comment');
  const username = formData.get('username');
  const postId = formData.get('post_id');

  await sql`INSERT INTO comments01 (username, comment, post_id)
       VALUES (${username}, ${comment}, ${postId}) `;

  revalidatePath(`/allPosts/${postId}`);
}

步骤3:修改原页面(服务器组件)

替换原有表单为客户端组件,简化页面逻辑:

import { notFound } from "next/navigation";
import Image from "next/image";
import Link from "next/link";
import { HiPencilAlt } from "react-icons/hi";
import RemoveBtn from "@/app/components/RemoveBtn";
import RemoveBtnComments from "@/app/components/RemoveBtnComments";
import CommentForm from "@/app/components/CommentForm";
import { sql } from '@vercel/postgres';

export default async function Page({ params }) {
  // 获取单篇文章及评论
  const post = (
    await sql`SELECT * FROM posts01 WHERE post_id = ${params.singlePost} `
  ).rows[0];
  const comments = (
    await sql`SELECT * FROM comments01 WHERE post_id = ${params.singlePost}`
  ).rows;

  if (!post) {
    notFound();
  }

  return (
    <div>
      {/* 文章内容区域 */}
      <div>
        <h2 className="text-2xl font-bold mb-4">{post.title}</h2>
        <div className="flex justify-end gap-5">
          <RemoveBtn post_id={post.post_id} />
          <Link href={`${params.singlePost}/editPost`}>
            <HiPencilAlt size={24} />
          </Link>
        </div>
        <h3 className="text-lg mb-4">{post.content}</h3>
        {post.image_url ? (
          <Image
            src={post.image_url}
            alt={`image of ${post.title}`}
            width={300}
            height={300}
          />
        ) : (
          <h3>Image unavailable</h3>
        )}
      </div>

      {/* 评论区域 */}
      <div>
        <h4 className="text-xl font-bold mb-4">Comments</h4>
        <div className="mb-4">
          <CommentForm postId={post.post_id} />
        </div>
        <div>
          {comments.map((comment) => (
            <div
              className="bg-gray-100 rounded-md p-4 mb-4"
              key={comment.comment_id}
            >
              <p className="font-bold text-lg mb-2">{comment.username}</p>
              <p>{comment.comment}</p>
              <div className="flex justify-end gap-5">
                <RemoveBtnComments comment_id={comment.comment_id} />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

关键说明

  • 客户端组件通过'use client'声明,可使用useState管理输入状态,提交后直接重置清空输入框。
  • 服务器动作单独抽离后,符合Next.js安全规范,且逻辑可复用。
  • 无需拆分整个页面,仅需把涉及客户端交互的表单部分拆成组件即可,Vercel数据库请求已通过服务器动作安全处理。

内容的提问来源于stack exchange,提问作者Robbie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 23:14:57