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

React应用表单提交异常:无法传数据到服务器且报TypeError错误

问题

提交用户详情表单创建订单时,持续收到错误:TypeError: Cannot read properties of null (reading 'id')。

应用运行逻辑

  • React组件内的表单提交时处理数据并发送至服务器创建订单
  • 服务器配置正常,用Postman发送请求可正常创建订单
  • React应用提交表单时,页面刷新但数据未发送到服务器,浏览器控制台出现上述错误

相关代码

React组件(Contact.js)

import React, { useState } from "react";
import Footer from "../../Components/Footer";
import { useSelector, useDispatch } from "react-redux";
import { getOrders } from "../../actions/orders.action";
import { createOrder } from "../../actions/order.action";
import { Link, useNavigate } from "react-router-dom";
import cookie from "js-cookie";

const Contact = () => {
  const cart = useSelector((state) => state.cartReducer);
  const [username, setUserName] = useState("");
  const [address, setAddress] = useState("");
  const [phoneNumber, setPhoneNumber] = useState("");
  const navigate = useNavigate();
  const dispatch = useDispatch();
  console.log(cart.cartId);
  const removeCookie = (key) => {
    if (window !== "undefined") {
      cookie.remove(key, { expire: 1 });
    }
  };

const handleSubmitOrder = async (e) => {
    e.preventDefault();  // 移除该行后页面仍刷新,但数据未发送
    if (username && address && phoneNumber) {
        // ... 表单数据处理 ...
        await dispatch(createOrder(data));
        // ... 清空表单 ...
        navigate("/commands");  // 跳转到其他页面
    } else {
        alert("Please fill out all the required information.");
    }
};
 return (
    <div>
      <header className="header">
        <div className="header__topbar">
          <Link to="/">
            <p className="header__topbar__logo">Panini</p>
          </Link>
          <div className="header__cart__desktop">
            <span className="header__topbar__cart__value"></span>
          </div>
        </div>
      </header>
      <main className="main">
        <section>
          <h1 className="form__title">Entrez vos informations</h1>
          <form className="form__container" onSubmit={handleSubmitOrder}>
            <div className="form__contain">
              <div className="form__input__container">
                <label htmlFor="name">Nom</label>
                <input
                  onChange={(e) => setUserName(e.target.value)}
                  value={username}
                  type="text"
                  id="name"
                  placeholder="Entrez votre nom"
                />
              </div>
              <div className="form__input__container">
                <label htmlFor="location">Quartier</label>
                <input
                  onChange={(e) => setAddress(e.target.value)}
                  value={address}
                  type="text"
                  id="location"
                  placeholder="Entrez le nom de votre quartier"
                />
              </div>
              <div className="form__input__container">
                <label htmlFor="number">N° de téléphone</label>
                <input
                  type="text"
                  onChange={(e) => setPhoneNumber(e.target.value)}
                  value={phoneNumber}
                  id="number"
                  placeholder="Entrez votre numéor de téléphone "
                />
              </div>
            </div>
            <input
              className="form__submit"
              type="submit"
              value="Valider votre commande"
            />
          </form>
        </section>
      </main>
      <Footer />
    </div>
  );
};

export default Contact;

Action(orders.action.js)

import axios from "axios";
export const CREATE_ORDER = "CREATE_ORDER";
export const CREATE_ORDER_SUCCESS = "CREATE_ORDER_SUCCESS";
export const CREATE_ORDER_FAILURE = "CREATE_ORDER_FAILURE";
// ... 其他Action代码 ...
export const createOrder = (data) => {
  return (dispatch) => {
    try {
      const response = axios.post(
        `${process.env.REACT_APP_API_URL}api/cart/create/orders`,
        data,
        { withCredentials: true }
      );

      dispatch({
        type: CREATE_ORDER_SUCCESS,
        payload: response.data,
      });
    } catch (error) {
      dispatch({
        type: CREATE_ORDER_FAILURE,
        payload: error.response.data,
      });
    }
  };
};

Reducer(order.reducer.js)

import {
  CREATE_ORDER_FAILURE,
  CREATE_ORDER_SUCCESS,
} from "../actions/order.action";
// ... 其他Reducer代码 ...
const initialState = {
  order: null,
  error: null,
};

export default function orderReducer(state = initialState, action) {
  switch (action.type) {
    case CREATE_ORDER_SUCCESS:
      return {
        ...state,
        order: action.payload, // 存储订单详情
        error: null,
      };
    case CREATE_ORDER_FAILURE:
      return {
        ...state,
        error: action.payload,
      };
    default:
      return state;
  }
};

后端(Node.js)createOrder控制器

module.exports.createOrder = async (req, res) => {
  const { cartId, username, phone_number, address, status, totalPrice } =
    req.body;

  try {
    const cart = await Cart.findByPk(cartId);
    const order = await OrderItem.findAll({
      where: { cartId: cart.id },
    });

    if (!cart.id) {
      return res.status(404).json("Cet article n'est plus disponible");
    }

    const total = await Promise.all(
      order.map(async (orderItemId) => {
        const products = await Product.findOne({
          where: { id: orderItemId.productId },
        });
        const options = await Option.findOne({
          where: { id: orderItemId.optionId },
        });
        const qty = await OrderItem.findOne({
          where: { quantity: orderItemId.quantity },
        });

        let prices;
        prices = products.price * qty.quantity;
        return prices;
      })
    );

    const totalPrices = total.reduce((a, b) => a + b, 0);

    const createOrder = await ShopOrder.create({
      orderId: cart.id,
      username,
      phone_number,
      address,
      status,
      totalPrice: totalPrices,
    });

    createOrder.save();
    res.cookie("cart", "", { maxAge: 1 });
    return res.status(201).json(createOrder);
  } catch (error) {
    return res.status(500).json({ "error": error.message });
  }
};

原因分析
  1. 前端数据缺失:handleSubmitOrder中data变量未定义就传给createOrder,导致后端接收不到cartId等关键参数。
  2. axios请求未异步处理:Action中的axios.post未加await,直接读取response.data时请求未完成,组件中await dispatch(...)也无法正确等待请求结束。
  3. 后端空值判断逻辑错误:先通过cart.id查询OrderItem,再判断!cart.id,若cart为null(前端未传cartId或无效),直接抛出Cannot read properties of null (reading 'id')错误。
  4. 表单刷新问题:核心是数据未正确发送,即使移除e.preventDefault(),无效请求也不会触发后端逻辑,页面仍会默认刷新。

解决方案

前端修复

  1. 完善表单数据处理:定义完整的data对象,包含后端所需字段:
const handleSubmitOrder = async (e) => {
    e.preventDefault();
    if (username && address && phoneNumber && cart.cartId) {
        const data = {
            cartId: cart.cartId,
            username,
            phone_number: phoneNumber,
            address,
            status: "pending", // 根据业务设置默认状态
            totalPrice: cart.totalPrice || 0 // 从redux购物车中获取总价
        };
        await dispatch(createOrder(data));
        // 清空表单
        setUserName("");
        setAddress("");
        setPhoneNumber("");
        navigate("/commands");
    } else {
        alert("请填写所有必填信息,且购物车不能为空");
    }
};
  1. 修复Action异步请求:给axios.post添加await,确保获取响应后再分发action:
export const createOrder = (data) => {
  return async (dispatch) => {
    try {
      const response = await axios.post(
        `${process.env.REACT_APP_API_URL}api/cart/create/orders`,
        data,
        { withCredentials: true }
      );

      dispatch({
        type: CREATE_ORDER_SUCCESS,
        payload: response.data,
      });
    } catch (error) {
      dispatch({
        type: CREATE_ORDER_FAILURE,
        payload: error.response?.data || "请求失败",
      });
    }
  };
};

后端修复

  1. 提前判断cart是否存在:在使用cart.id前先检查cart有效性:
const cart = await Cart.findByPk(cartId);
if (!cart) {
  return res.status(404).json("购物车不存在");
}
const order = await OrderItem.findAll({
  where: { cartId: cart.id },
});
// 移除多余的if (!cart.id)判断
  1. 优化总价计算逻辑:直接使用orderItem的属性,无需重复查询:
const total = await Promise.all(
  order.map(async (orderItem) => {
    const product = await Product.findByPk(orderItem.productId);
    if (!product) return 0;
    return product.price * orderItem.quantity;
  })
);
  1. 移除多余的createOrder.save():ShopOrder.create已自动保存数据,无需重复调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:30:53