为什么fetch返回更新state会渲染全对象数组及坐标反查城市名错位
问题成因
fetch属于异步网络请求,响应返回顺序不受代码遍历顺序控制:你按id1、id2、id3的顺序发起请求,很可能id2的请求先拿到结果,你如果按返回顺序往数组里塞值,自然就会出现和workout顺序不匹配的错位问题- 城市名存储逻辑错误:原有代码没有把城市名和对应workout的id做绑定,不管是直接覆盖state为单元素数组,还是按返回顺序存到数组里,都无法保证城市名和对应的workout正确匹配
- 多余的动态key导致强制重绘:外层div绑定了每次渲染都会变化的随机key,每次state更新都会导致整个列表完全销毁重绘,进一步加剧了渲染异常
修复方案
核心思路是将城市名与workout id强绑定,完全不受异步请求返回顺序的影响:
- 把存储城市名的state修改为对象结构,以workout id为key存储对应城市名
- 请求拿到结果后,直接按对应workout id更新state,不需要考虑返回顺序
- 渲染时按当前遍历的workout id读取对应的城市名即可
- 增加请求清理逻辑,避免组件卸载后仍更新state导致的内存泄漏
修复后代码
import React, { useEffect, useState } from "react"; import "./styles.css"; import { WorkoutsData } from "./WorkoutLocation"; const App = () => { const [workouts, setWorkouts] = useState(WorkoutsData); // 用对象存储,key为workout id,value为对应的城市名 const [cityMap, setCityMap] = useState({}); useEffect(() => { const controllers = []; workouts.forEach((workout) => { const controller = new AbortController(); controllers.push(controller); fetch( `https://geocode.xyz/${workout.coords.latitude},${workout.coords.longitude}?geoit=json`, { signal: controller.signal } ) .then((res) => res.json()) .then((result) => { const fullLocation = `${result.staddress || ""}, ${result.city || ""}`.trim(); // 按id更新,不会出现错位 setCityMap((prev) => ({ ...prev, [workout.id]: fullLocation })); }) .catch((err) => { if (err.name !== "AbortError") console.error("位置获取失败:", err); }); }); // 清理未完成的请求 return () => controllers.forEach((c) => c.abort()); }, [workouts]); return ( <div> {workouts.map((workout) => ( <h2 key={workout.id}> {cityMap[workout.id] || "加载位置中..."} </h2> ))} </div> ); }; export default App;
内容的提问来源于stack exchange,提问作者prakash subba
相关产品推荐
相关产品推荐

