React组件挂载早于API响应,舞蹈数据无法渲染问题排查
问题:API获取舞蹈数据前useEffect已执行,导致数据无法正常显示
我希望展示通过API调用获取的舞蹈数据,但useEffect在API返回响应前就已执行,导致数据无法正常显示。已附上代码,希望了解问题所在及此类场景的最佳实践,不想在函数内直接返回HTML。
function Home() { const userId = useSelector((state: RootState) => state.user.id); const [dances, setDances] = useState<DanceCardProps[]>([]) interface DanceCardProps { picture?: string; name: string; danceId: string; description?: string; author: string; year?: number; } const fetchDanceData = async() => { axios.get(process.env.REACT_APP_LOCAL_API_URL + "/app/getDances?accountId=1", { headers: {EXAMPLE, }, }) .then((res) => { console.log("res: ", res); var newArray = dances as DanceCardProps[] res.data.map((obj: DanceCardProps )=> { console.log(obj) const newDance = { danceId: obj.danceId, name: obj.name, author: obj.author } newArray.push(newDance) }) setDances(newArray as DanceCardProps[]) console.log("dances: ",dances) }) .catch((e) => { console.log("error: ", e); }); }; useEffect(function(){ fetchDanceData() },[dances]) return ( <> <Stack className="wrapper"> <FilterRow func={fetchDanceData} /> <Divider /> <Grid container spacing={3} className="home-grid" direction="row"> {dances.map((dance: DanceCardProps) => { return ( <DanceCard name={dance.name} id={dance.danceId} author={dance.author} year={dance.year} /> ); })} </Grid> </Stack> </> ); }

问题分析与解决方案
核心问题
- useEffect依赖错误:将
dances作为依赖项,会导致每次状态更新都触发重新请求,形成无限循环;且初始请求是异步的,组件会先渲染空数组,这是正常流程,但后续状态更新逻辑出错导致数据无法展示。 - 状态更新违反不可变原则:直接修改原状态数组(
newArray = dances后push),React无法检测到状态引用变化,因此不会触发组件重新渲染展示新数据。另外,console.log(dances)打印的是旧状态,因为setState是异步操作。
修复后的代码
function Home() { const userId = useSelector((state: RootState) => state.user.id); const [dances, setDances] = useState<DanceCardProps[]>([]) interface DanceCardProps { picture?: string; name: string; danceId: string; description?: string; author: string; year?: number; } const fetchDanceData = async() => { try { const res = await axios.get(`${process.env.REACT_APP_LOCAL_API_URL}/app/getDances?accountId=1`, { headers: { EXAMPLE }, }); console.log("res: ", res); // 基于响应数据创建新数组,不修改原状态 const newDances = res.data.map((obj: DanceCardProps )=> ({ danceId: obj.danceId, name: obj.name, author: obj.author, year: obj.year })); setDances(newDances); } catch (e) { console.log("error: ", e); } }; // 仅组件挂载时执行一次,若需根据用户切换重新请求,可添加userId到依赖数组 useEffect(() => { fetchDanceData(); }, []); return ( <> <Stack className="wrapper"> <FilterRow func={fetchDanceData} /> <Divider /> <Grid container spacing={3} className="home-grid" direction="row"> {dances.map((dance: DanceCardProps) => ( <DanceCard key={dance.danceId} // 添加唯一key,避免React渲染警告 name={dance.name} id={dance.danceId} author={dance.author} year={dance.year} /> ))} </Grid> </Stack> </> ); }
此类场景的最佳实践
- 精准设置useEffect依赖:仅在组件挂载时请求数据用空依赖数组
[];若需在特定变量(如userId)变化时重新请求,将该变量加入依赖数组。 - 严格遵循状态不可变:更新数组/对象时,通过
map、扩展运算符[...arr]等方式创建新引用,禁止直接修改原状态。 - 用async/await简化异步逻辑:替代
then/catch让代码更易读,同时统一处理请求错误。 - 添加加载状态(可选):API请求较慢时,通过
isLoading状态展示加载提示,提升用户体验:const [isLoading, setIsLoading] = useState(true); const fetchDanceData = async() => { setIsLoading(true); try { // 请求逻辑 setDances(newDances); } catch (e) { // 错误处理 } finally { setIsLoading(false); } }; // 在return中优先判断加载状态 if (isLoading) return <div>加载中...</div>; - 列表项必须加唯一key:这是React列表渲染的强制要求,能避免渲染警告并提升性能。
内容的提问来源于stack exchange,提问作者Gaspar Luik
相关产品推荐
相关产品推荐

