React中POST请求后如何用useEffect即时获取数据及方案验证
实现方案评估
你的核心实现思路(用状态变化触发useEffect重新拉取数据)是可行的,但现有代码存在几个会导致功能异常的问题,修正后即可稳定运行。
现存问题
- 时序错误:提交表单时你先触发刷新状态、再调用添加接口,而axios请求是异步的,会导致拉取数据的请求比提交请求更早到达服务端,大概率拿到的还是提交前的旧数据,极端情况还会触发服务端报错。
- 状态语义模糊:用布尔值
send作为刷新触发标识,后续代码迭代时很难快速理解这个状态的作用,直接把setSend方法透传给业务组件也不符合状态封装原则。 - 请求配置有误:提交普通JSON数据时强行设置了
multipart/form-data请求头,这个头是用于传输文件/表单格式数据的,普通JSON请求用axios默认的application/json即可,错误配置头可能导致后端解析参数失败。 - 缺少交互兜底:没有提交加载状态、错误提示,用户重复点击提交会发送多次重复请求,提交失败时用户也无法感知。
修正后的代码
1. Context 状态逻辑调整
把布尔触发值改成语义更清晰的递增计数标识,封装刷新方法不直接暴露setState,调整请求触发时机:
import { createContext, useState, useEffect } from "react"; import { apiService } from "../services/api/api.service"; const Context = createContext({ footer: false, subjectForm: false, storedSubjects: [], footerVisibilityHandler: () => {}, subjectFormVisibilityHandler: () => {}, refreshSubjects: () => {}, }); export const ContextProvider = ({ children }) => { const [footer, setFooter] = useState(false); const [subjectForm, setSubjectForm] = useState(false); const [storedSubjects, setStoredSubjects] = useState([]); // 用递增数字作为刷新触发key,语义更清晰 const [refreshKey, setRefreshKey] = useState(0); useEffect(() => { const getData = async () => { try { const getSubjects = await apiService.getSubjects(); const tableRow = getSubjects.data.map((subject) => ({ name: subject.name, })); setStoredSubjects(tableRow); } catch (err) { console.error("拉取科目列表失败:", err); // 可在此处添加全局错误提示 } }; getData(); }, [refreshKey]); const footerVisibilityHandler = () => { setFooter((previousState) => !previousState); setSubjectForm(false); }; const subjectFormVisibilityHandler = () => { setSubjectForm((previousState) => !previousState); }; const context = { footer, subjectForm, storedSubjects, footerVisibilityHandler, subjectFormVisibilityHandler, // 封装刷新方法,不直接暴露setState refreshSubjects: () => setRefreshKey(prev => prev + 1), }; return <Context.Provider value={context}>{children}</Context.Provider>; }; export default Context;
2. 表单组件提交逻辑调整
等添加接口请求成功后再触发列表刷新,加提交loading状态防止重复提交:
import Context from "../../store/context"; import FormContainer from "../UI/FormContainer"; import { apiService } from "../../services/api/api.service"; import { useContext, useRef, useState } from "react"; const AddSubject = () => { const ctx = useContext(Context); const subject = useRef(""); const [submitting, setSubmitting] = useState(false); const sendData = async (e) => { e.preventDefault(); const subjectName = subject.current.value.trim(); if (!subjectName) return; setSubmitting(true); try { // 等待提交成功后再刷新列表 await apiService.addSubject(subjectName); ctx.refreshSubjects(); // 提交成功后清空输入框、关闭表单 subject.current.value = ""; ctx.subjectFormVisibilityHandler(); } catch (err) { console.error("添加科目失败:", err); // 可在此处添加用户可见的错误提示 } finally { setSubmitting(false); } }; return ( <FormContainer show={ctx.subjectForm} send={sendData}> <label htmlFor="subject">Subject Name: </label> <input type="text" id="subject" ref={subject} disabled={submitting} /> <button type="submit" disabled={submitting}> {submitting ? "提交中..." : "Add"} </button> </FormContainer> ); }; export default AddSubject;
3. API 配置调整
删除不必要的请求头配置:
import axios from "axios"; const api = () => { const baseUrl = "https://localhost:5001/api"; // 普通JSON请求不需要自定义请求头,axios默认配置即可满足 return { getSubjects: () => axios.get(`${baseUrl}/subjectcontroller/getsubjects`), addSubject: (subjectName) => axios.post(`${baseUrl}/subjectcontroller/createsubject`, { name: subjectName, }), }; }; export const apiService = api();
额外优化建议
如果后续项目里同类数据刷新场景变多,可以把数据拉取、loading、错误处理都封装成通用的自定义hook,不用每个列表都单独写一套刷新触发状态,维护成本会低很多。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

