React创建订单组件useEffect重复提交数据问题求助
React useEffect 导致重复创建订单及循环执行问题解决
问题原因
1. 数据库新增2条订单
React 18+的开发环境默认开启严格模式(Strict Mode),这个模式会主动触发两次挂载阶段的副作用(包括useEffect回调),用来检测代码中的潜在问题。你的POST创建订单请求属于副作用,因此会被执行两次,导致数据库生成两条重复订单。生产环境不会有这个问题,Strict Mode仅在开发时生效。
2. fetchOrder函数循环执行
如果fetchOrder没有用useCallback包裹,组件每次重新渲染时都会生成一个全新的函数引用。如果你的useEffect依赖数组中包含了这个不稳定的函数引用,就会导致useEffect反复触发,进而循环调用fetchOrder。
具体解决方案
方案1:解决开发环境重复创建订单问题
用useRef标记请求是否已执行,避免Strict Mode的双重调用:
import { useEffect, useRef, useState } from 'react'; // ... const isOrderCreated = useRef(false); const [order, setOrder] = useState(null); useEffect(() => { if (!isOrderCreated.current) { fetch(`${process.env.REACT_APP_API_URL}/orders`, { method: 'POST', headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, }) .then(res => res.json()) .then(data => { setOrder(data); isOrderCreated.current = true; }) .catch(err => { console.error(err); isOrderCreated.current = true; }); } }, []);
方案2:修复fetchOrder循环执行问题
用useCallback包裹fetchOrder,保证函数引用稳定,同时正确设置useEffect依赖:
import { useEffect, useCallback, useState } from 'react'; // ... const [order, setOrder] = useState(null); const fetchOrder = useCallback(() => { fetch(`${process.env.REACT_APP_API_URL}/orders`, { method: 'POST', headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, }) .then(res => res.json()) .then(data => setOrder(data)) .catch(err => console.error(err)); }, []); // 如果token会动态变化,需要把token加入依赖数组 useEffect(() => { fetchOrder(); }, [fetchOrder]);
方案3:更合理的业务逻辑(推荐)
创建订单这类修改数据的操作,不应该在组件挂载时自动执行,而是由用户主动操作触发(比如点击按钮),从根源避免重复请求:
import { useState } from 'react'; // ... const [order, setOrder] = useState(null); const handleCreateOrder = () => { fetch(`${process.env.REACT_APP_API_URL}/orders`, { method: 'POST', headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, }) .then(res => res.json()) .then(data => setOrder(data)) .catch(err => console.error(err)); }; // 渲染按钮 return ( <div> <button onClick={handleCreateOrder}>创建订单</button> {order && <p>订单创建成功:{order.id}</p>} </div> );
内容的提问来源于stack exchange,提问作者Matthew Raga
相关产品推荐
相关产品推荐

