React结合Node.js、MySQL开发增改功能时出现data.record未定义的map访问报错
报错根因排查
核心原因
- 函数组件
useState的状态更新方法setData为全量替换状态,不会像类组件setState那样自动浅合并原有状态字段。你在多处调用setData时未保留原有状态中的record字段,导致状态被覆盖后record属性丢失,变为undefined。 - 异步请求时序问题:
updateTask中状态更新完成后立即调用异步的fetchData拉取列表,在请求返回拿到新的record数据前,组件触发重渲染时直接执行data.record.map,此时record已被之前的状态更新清除,直接触发报错。 - 列表渲染逻辑未加兜底判断,未对
record的类型、存在性做校验,直接调用map方法。
具体问题点定位
updateTask方法中的setData调用未包含record字段,直接传入仅含提示、表单状态的对象,导致状态更新后data.record属性直接消失fetchData、addData、editTask中的setData调用均采用直接传全新对象的写法,未保留原有其他状态字段,容易引发属性丢失的连锁问题- 列表渲染逻辑未做边界判断
editTask方法中错误将全量列表用的record属性覆盖为单条查询的返回结果,打乱原有列表数据
修复方案
1. 状态更新逻辑修正
所有调用setData的场景,都通过回调拿到之前的状态值,用展开语法保留原有状态字段,仅更新需要变更的属性:
- 避免状态全量替换导致的字段丢失
- 对接口返回的
record值做空数组兜底,避免值为undefined - 移除
editTask中对record的错误覆盖,保留原有全量列表数据
修正后的各方法代码
// 全量拉取列表方法修正 const fetchData = () => { var headers = new Headers(); headers.append("Content-Type", "application/json"); fetch("http://localhost:8000/api/view", { method: "GET", headers: headers, }).then((res) => res.json()) .then((result) => { setData((preValue) => ({ ...preValue, // 保留原有所有状态 record: result.res || [], // 空值兜底 })) }).catch((err) => console.log("Error", err)) }; // 新增数据方法修正 const addData = () => { var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json") var body = JSON.stringify({ name: data.name, location: data.location }); fetch("http://localhost:8000/api/create", { method: "POST", headers: myHeaders, body: body }).then((res) => res.json()) .then((result) => { setData((preValue) => ({ ...preValue, name: "", location: "", showAlert: true, alertMsg: result.res, alertType: "success", record: result.res || [], })); }); }; // 编辑方法修正 const editTask = (id) => { fetch("http://localhost:8000/api/view/" + id, { method: "GET", }).then((res) => res.json()) .then((result) => { setData((preValue) => ({ ...preValue, id: id, update: true, // 仅更新表单字段,不覆盖原有的record列表数据 name: result.res[0].name, location: result.res[0].location, })); }).catch((err) => console.log("Error", err)); }; // 更新提交方法修正 const updateTask = () => { var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); var body = JSON.stringify({ id: data.id, name: data.name, location: data.location }); fetch("http://localhost:8000/api/update", { method: "PUT", headers: myHeaders, body: body }).then((res) => res.json()) .then((result) => { setData((preValue) => ({ ...preValue, // 关键:保留原有的record字段,避免render时record丢失 showAlert: true, alertMsg: result.res, alertType: "success", update: false, id: "", name: "", location: "", })); fetchData(); }).catch((err) => console.log("Error", err)); };
2. 列表渲染逻辑加兜底判断
在调用map前校验record为数组类型,避免undefined触发报错:
// 修正后的列表渲染逻辑 { Array.isArray(data.record) ? data.record.map((record) => { return ( <tr> <td>{record.id}</td> <td>{record.name}</td> <td>{record.location}</td> <td> <Button variant="info" onClick={() => editTask(record.id)}>Edit</Button> </td> <td> <Button variant="danger">Delete</Button> </td> </tr> ); }) : <tr><td colSpan={5}>数据加载中...</td></tr> }
内容的提问来源于stack exchange,提问作者Bhavik Sejpal
相关产品推荐
相关产品推荐

