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

如何实现NextJS表单提交与MongoDB关联?现有代码问题求助

解决Next.js表单与MongoDB连接问题及推荐Hooks使用

一、现有代码的核心问题

  • 表单提交路径不匹配:表单action="/api/form",但你的API路由实际是/api/products
  • 提交按钮未关联表单:submit按钮在<footer>标签内,不属于<form>节点,无法触发表单提交事件
  • 输入字段与模型字段不对应:模型里是cover字段,但输入框name设为image;模型包含wallet字段,但表单未提供对应输入项
  • 未阻止默认提交行为:直接将postProduct绑定到onSubmit,未阻止浏览器默认的刷新提交逻辑,且异步提交未正确处理
  • 无表单状态管理:未使用Hooks收集输入值,无法将正确的结构化数据传递给API

二、推荐使用的React Hooks

  • useState:管理表单各字段的状态,方便实时收集和修改输入数据
  • useCallback:优化表单提交函数,避免组件重渲染时重复创建函数(可选但推荐)

三、修改后的完整代码示例

1. 表单组件(NewProductForm.js)

import NewProductTabs from "./NewProductTabs";
import React, { useState, useCallback } from "react";
import { postProduct } from "../../../database/helpers/helpers";

export default function NewProductForm() {
  // 初始化表单状态,与MongoDB模型字段一一对应
  const [formData, setFormData] = useState({
    cover: "",
    title: "",
    description: "",
    url: "",
    price: "",
    wallet: ""
  });

  // 实时更新表单输入状态
  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };

  // 表单提交处理逻辑
  const handleSubmit = useCallback(async (e) => {
    e.preventDefault(); // 阻止浏览器默认提交行为
    try {
      const result = await postProduct(formData);
      console.log("产品添加成功:", result);
      // 提交成功后重置表单
      setFormData({
        cover: "",
        title: "",
        description: "",
        url: "",
        price: "",
        wallet: ""
      });
    } catch (error) {
      console.error("提交失败:", error);
    }
  }, [formData]);

  return (
    <div className="flex-col gap-4">
      <NewProductTabs />
      <form
        onSubmit={handleSubmit}
        className="flex flex-col gap-3 border border-black p-6 pb-10"
      >
        {/* Cover image 字段,name对应模型的cover */}
        <div className="input-type gap-2">
          <label className="flex px-5 py-2 text-sm font-semibold">
            Cover image
          </label>
          <input
            type="text"
            name="cover"
            value={formData.cover}
            onChange={handleInputChange}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="Image link"
            required
          />
        </div>
        {/* Title字段 */}
        <div className="input-type gap-2">
          <label className="flex px-5 py-2 text-sm font-semibold">Title</label>
          <input
            type="text"
            name="title"
            value={formData.title}
            onChange={handleInputChange}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="Title"
            required
          />
        </div>
        {/* Description字段 */}
        <div className="input-type">
          <label className="flex px-5 py-2 text-sm font-semibold">
            About Product
          </label>
          <textarea
            rows="6"
            name="description"
            value={formData.description}
            onChange={handleInputChange}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="Description"
            required
          />
        </div>
        {/* Url字段 */}
        <div className="input-type">
          <label className="flex px-5 py-2 text-sm font-semibold">Url</label>
          <input
            type="url"
            name="url"
            value={formData.url}
            onChange={handleInputChange}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="/url"
            required
          />
        </div>
        {/* Price字段:模型为String类型,输入number后转字符串 */}
        <div className="input-type">
          <label className="flex px-5 py-2 text-sm font-semibold">Price</label>
          <input
            type="number"
            name="price"
            value={formData.price}
            onChange={(e) => setFormData(prev => ({ ...prev, price: e.target.value.toString() }))}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="Price, USDT"
            required
          />
        </div>
        {/* Wallet字段:补充模型要求的字段 */}
        <div className="input-type gap-2">
          <label className="flex px-5 py-2 text-sm font-semibold">Wallet Address</label>
          <input
            type="text"
            name="wallet"
            value={formData.wallet}
            onChange={handleInputChange}
            className="w-full rounded-md border border-black px-5 py-5 text-sm shadow-brutal-sm focus:outline-none"
            placeholder="Wallet address"
            required
          />
        </div>
        {/* 提交按钮移至表单内部 */}
        <footer className="flex h-20 mt-4">
          <button
            type="submit"
            className="flex w-full place-content-center bg-black p-2 text-sm font-bold"
          >
            <span className="flex items-center text-center text-white">
              Add product
            </span>
          </button>
        </footer>
      </form>
    </div>
  );
}

2. 辅助函数(helpers.js)修正

原代码中response.json()未加await,会返回Promise而非实际数据,需修正:

const BASE_URL = "http://localhost:3000";

//Add new product
export async function postProduct(formData) {
  try {
    const options = {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(formData),
    };

    const response = await fetch(`${BASE_URL}/api/products`, options);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const json = await response.json(); // 添加await获取实际数据

    return json;
  } catch (error) {
    console.error("提交错误:", error);
    throw error; // 抛出错误让组件处理
  }
}

3. 控制器(postProduct)优化

统一使用async/await风格,避免回调与async混用:

import Products from "../models/productModel";

//POST .../api/product
async function postProduct(req, res) {
  try {
    const formData = req.body;
    if (!formData) {
      return res.status(400).json({ error: "Form data isn't provided" });
    }
    const data = await Products.create(formData);
    return res.status(201).json(data); // 用201状态码表示资源创建成功
  } catch (error) {
    console.error("添加产品错误:", error);
    return res.status(500).json({ error: "Error while creating product" });
  }
}

export { postProduct };

四、关键修改说明

  1. 状态绑定:用useState创建与模型匹配的表单状态对象,每个输入框绑定value和onChange实现实时数据同步
  2. 提交逻辑:自定义handleSubmit函数阻止默认刷新,异步调用API并处理成功/失败场景
  3. 字段对齐:修正输入框name属性,确保与MongoDB模型字段完全对应
  4. 按钮位置:将提交按钮移入<form>内部,保证触发表单提交事件
  5. API调用优化:给response.json()添加await,并增加HTTP状态码校验,提升错误处理能力

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:05:35