You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React useState数组不更新:API数据添加仅显最后一条的原因及解决方案

React中调用API更新数组state仅显示最后一条数据的问题解决

这个问题我之前也碰到过,核心是React useState的异步更新特性导致的,我来帮你拆解原因并给出可行的解决方案。

问题重现

你尝试通过API获取多个城市的天气数据,想把每条返回的数据添加到weatherData数组state中,但最终只显示最后一条数据,你的代码如下:

import React, {useEffect, useState} from 'react';
import './App.css';
const data = require('../src/data/Step1'); //导入JSON文件
const axios = require("axios");

function App() {
  //用于存储天气数据的React Hook
  const [weatherData, setWeatherData] = useState([]);

  //调用getData函数的useEffect
  useEffect(() => {
    //逐个调用getData传入城市编码
    data.List.map(city => getData(city.CityCode));
  }, []);

  //getData函数
  async function getData(cityCode) {
    //Axios发送HTTP GET请求
    const response = await axios.get('http://api.openweathermap.org/data/2.5/group?id=' + cityCode + '&units=metric&appid=24e343673cb58392072a12e4705b1260');
    //将每个天气对象添加至weatherData数组
    setWeatherData([...weatherData,response.data.list[0]]);
  }

  if (weatherData.length > 1) {
    return (
      <div>
        { 
          <div className="container mt-5">
            {weatherData.map(x => (
              <div className="row mt-2 ">
                Id : {x.data.id} {" | "} Name :{x.data.name} {" | "} Description : {x.data.weather[0].description} {" | "} Temperature : {x.data.main.temp}
              </div>
            ))}
          </div>
        }
      </div>
    );
  } else {
    return (
      <div>
        <h1>Loading</h1>
      </div>
    )
  }
}

export default App;

问题原因

这个问题的根源是useState的异步更新与闭包特性:

  • React的useState更新是异步的,而且每次更新都会捕获当前作用域中的state快照。
  • 你的data.List.map会同时触发多个异步的getData请求,这些请求几乎是并行发起的。当每个请求完成调用setWeatherData时,它们捕获到的weatherData都是初始的空数组[],所以每次更新都会生成一个只包含当前请求数据的新数组([...[], newData])。最后所有更新完成后,只有最后一次的更新会生效,导致weatherData中只有最后一条数据。

解决方案

我给你提供两种可行的解决方案,你可以根据需求选择:

方案1:使用函数式更新(推荐,实时添加数据)

React允许给setState传递一个函数,这个函数会接收当前最新的state作为参数,返回新的状态。这样就能确保每次更新都是基于最新的数组状态,而不是旧的快照。

修改getData函数中的setWeatherData部分:

async function getData(cityCode) {
  const response = await axios.get('http://api.openweathermap.org/data/2.5/group?id=' + cityCode + '&units=metric&appid=24e343673cb58392072a12e4705b1260');
  // 使用函数式更新,基于最新的prevState来添加新数据
  setWeatherData(prevWeatherData => [...prevWeatherData, response.data.list[0]]);
}

方案2:批量请求后一次性更新(适合不需要实时渲染的场景)

如果不需要每条数据返回就实时渲染,而是等所有请求完成后统一展示,可以使用Promise.all来等待所有API请求完成,然后一次性更新state,这样还能减少state更新的次数,提升性能。

修改useEffect和getData部分:

useEffect(() => {
  // 定义异步函数处理所有请求
  const fetchAllWeatherData = async () => {
    // 生成所有API请求的Promise数组
    const weatherPromises = data.List.map(city => 
      axios.get(`http://api.openweathermap.org/data/2.5/group?id=${city.CityCode}&units=metric&appid=24e343673cb58392072a12e4705b1260`)
        .then(response => response.data.list[0]) // 提取需要的天气数据
    );
    // 等待所有请求完成,获取完整的数据数组
    const allWeatherData = await Promise.all(weatherPromises);
    // 一次性更新state
    setWeatherData(allWeatherData);
  };

  fetchAllWeatherData();
}, []);

// 可以删除原来的getData函数

额外修复:渲染代码的小问题

你的渲染部分还有一个错误:x.data.id应该改成x.id,因为response.data.list[0]已经是天气对象本身,不需要再取data属性。修改后的渲染代码:

{weatherData.map(x => (
  <div className="row mt-2 ">
    Id : {x.id} {" | "} Name :{x.name} {" | "} Description : {x.weather[0].description} {" | "} Temperature : {x.main.temp}
  </div>
))}

内容的提问来源于stack exchange,提问作者Rashmika Gamage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:38:15