React POST请求中使用formData.append()致事件起止时间异常
问题
开发React项目时,创建事件并通过POST请求将事件详情(含JSON对象)和活动海报文件提交到后端,使用formData.append()处理传输。但遇到事件起止时间无法正确保存的问题:调用formData.append()前起止时间设置正确,调用后时间变为当前时间,仅日期保留设置值。
以下是使用的代码:
export default function AddEvent() { let navigate = useNavigate(); const [event, setEvent] = useState({ eventName: "", eventType: "", eventLocationType: "", }); const [location, setLocation] = useState(""); const [startTime, setStartTime] = useState(moment()); const [endTime, setEndTime] = useState(moment()); const [selectedImage, setSelectedImage] = useState(undefined); const [imageUrl, setImageUrl] = useState(null); const { eventName, eventType, eventLocationType } = event; const onInputChange = (e) => { if (e && e.target && e.target.name) { setEvent({ ...event, [e.target.name]: e.target.value }); } }; const handleLocationChange = (event, value) => { setLocation(event.target.value); }; const handleStartTimeChange = (event) => { if (event && event.target && event.target.name) { setStartTime(moment(event.target.value)); //setStartTime(event.target.value); } }; const handleEndTimeChange = (event) => { if (event && event.target && event.target.name) { setEndTime(moment(event.target.value)); //setEndTime(event.target.value); } }; const onSubmit = async (e) => { e.preventDefault(); console.log({ ...event, location, startTime, endTime, selectedImage }); const formData = new FormData(); formData.append("file", selectedImage); formData.append( "body", JSON.stringify({ ...event, location, startTime, endTime, }) ); for (let pair of formData.entries()) { console.log(pair[0] + ": " + pair[1]); } await axios .post("http://localhost:8080/api/event/saveEvent", formData, { headers: { "Content-Type": "multipart/form-data", }, }) .then((response) => { console.log(response); Swal.fire({ title: "Event saved successfully!", icon: "success", confirmButtonText: "OK", }).then(() => { navigate("/"); }); }) .catch((error) => { console.log(error); }); }; return ( <div style={{ paddingBottom: "30px" }}> <FormControl encType="multipart/form-data" onSubmit={(e) => onSubmit(e)} sx={{ display: "flex", width: "50%", paddingLeft: "250px" }} > <FormLabel disabled={true} sx={{ paddingBottom: "10px", display: "flex", justifyContent: "flex-start", }} > Event Start Date & Time </FormLabel> <LocalizationProvider dateAdapter={AdapterDayjs}> <DemoContainer components={["DateTimeField"]}> <DateTimeField size="small" label="Start Date & Time" name="startTime" value={startTime} onChange={handleStartTimeChange} sx={{ "& .Mui-focus": { "&:focus": { outline: "none", color: "grey", }, } }} /> </DemoContainer> </LocalizationProvider> </div> <div> <FormLabel disabled={true} sx={{ paddingBottom: "10px", display: "flex", justifyContent: "flex-start", }} > Event End Date & Time </FormLabel> <LocalizationProvider dateAdapter={AdapterDayjs}> <DemoContainer components={["DateTimeField"]}> <DateTimeField size="small" label="End Date & Time" name="endTime" value={endTime} onChange={handleEndTimeChange} /> </DemoContainer> </LocalizationProvider> </div> </div> <Box sx={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", }} > <Button variant="outlined" onClick={onCancel} style={{ color: "#e6b81a", borderColor: "#e6b81a" }} > Cancel </Button> <Button variant="contained" onClick={onSubmit} style={{ backgroundColor: "#e6b81a", marginLeft: "10px" }} > Save </Button> </Box> </FormControl> </div> ); }
解决方案
问题根源
startTime和endTime存储的是moment对象,而JSON.stringify()无法正确序列化moment对象,会将其转换为包含当前时间的默认格式(仅保留日期部分,时间重置为当前)。
修复步骤
将moment对象转换为ISO格式字符串
在序列化到JSON前,把startTime和endTime转换为标准的ISO时间字符串,确保后端能正确解析:formData.append( "body", JSON.stringify({ ...event, location, startTime: startTime.toISOString(), // 转换为ISO字符串 endTime: endTime.toISOString(), // 转换为ISO字符串 }) );统一日期库(可选但推荐)
代码中同时使用了moment和dayjs(Material UI的LocalizationProvider用了AdapterDayjs),可能导致兼容性问题。建议统一使用其中一个库:- 继续用moment:将Material UI的适配器改为
AdapterMoment - 改用dayjs:将所有
moment()调用替换为dayjs(),并使用dayjs的toISOString()方法
- 继续用moment:将Material UI的适配器改为
验证表单提交前的时间值
可以在onSubmit中先打印转换后的时间,确认格式正确:console.log("转换后的开始时间:", startTime.toISOString()); console.log("转换后的结束时间:", endTime.toISOString());
额外优化
- 避免在
useState初始值中直接使用moment(),因为每次组件渲染都会创建新的moment对象,建议用初始ISO字符串或空值:const [startTime, setStartTime] = useState(moment().toISOString()); // 或者如果需要初始为空: const [startTime, setStartTime] = useState(null); - 确保后端能正确解析ISO格式的时间字符串,比如Java后端可以用
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")注解映射。
内容的提问来源于stack exchange,提问作者Vidushika Dasanayka
相关产品推荐
相关产品推荐

