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 }); } };
原因分析
- 前端数据缺失:
handleSubmitOrder中data变量未定义就传给createOrder,导致后端接收不到cartId等关键参数。 - axios请求未异步处理:Action中的
axios.post未加await,直接读取response.data时请求未完成,组件中await dispatch(...)也无法正确等待请求结束。 - 后端空值判断逻辑错误:先通过
cart.id查询OrderItem,再判断!cart.id,若cart为null(前端未传cartId或无效),直接抛出Cannot read properties of null (reading 'id')错误。 - 表单刷新问题:核心是数据未正确发送,即使移除
e.preventDefault(),无效请求也不会触发后端逻辑,页面仍会默认刷新。
解决方案
前端修复
- 完善表单数据处理:定义完整的
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("请填写所有必填信息,且购物车不能为空"); } };
- 修复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 || "请求失败", }); } }; };
后端修复
- 提前判断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)判断
- 优化总价计算逻辑:直接使用
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; }) );
- 移除多余的
createOrder.save():ShopOrder.create已自动保存数据,无需重复调用。
内容的提问来源于stack exchange,提问作者Oumar MAURET
相关产品推荐
相关产品推荐

