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

React监听props变化触发useEffect出现API死循环如何解决

死循环根本原因

触发死循环的核心问题有3个:

  1. 渲染阶段直接调用了修改状态的API请求函数
    你在loader为false的else分支里直接执行callApi(),而callApi请求成功后会调用setLoader(true)修改状态,状态修改立刻触发组件重新渲染。如果此时loader又变成false,就会再次进入else分支调用callApi,形成最直接的循环触发路径。
  2. useEffect逻辑和状态更新逻辑冲突
    你在useEffect里同时执行callApi()和setLoader(false):callApi请求成功会把loader设为true,useEffect又把loader设为false,二者反复拉扯触发重渲染。
  3. props依赖引用不稳定,重复触发请求
    如果父组件传递的props.coordinates是每次渲染都新生成的对象(比如父组件直接写coordinates={{lat: 1, lon: 2}}没有做memo优化),会导致useEffect的依赖每次都判定为变化,反复执行useEffect回调。同时useEffect里已经调用了一次callApi,else分支又调用一次,等于每次触发循环都会发2次请求,自然很快把API配额耗光触发封禁。

修复方案

修改后的完整代码如下:

import React, { useEffect, useState } from "react";
import axios from "axios";
import ForcastHour from "./ForcastHour";
import "./WeatherHourlyForcast.css";

const WeatherHourlyForcast = (props) => {
  const [loader, setLoader] = useState(false);
  const [hourlyForcastData, setHourlylyForcastData] = useState(null);

  // 仅在经纬度数值变化时触发请求,初始挂载也会执行一次
  useEffect(() => {
    // 发起请求前先把loader设为false,避免旧数据展示
    setLoader(false);
    callApi();
  // 用基础类型做依赖比引用类型对象更稳定,避免不必要的触发
  }, [props.coordinates.lat, props.coordinates.lon]);

  const showHourlyForcast = (response) => {
    console.log("showHourlyForcast", response.data.hourly);
    setHourlylyForcastData(response.data.hourly);
    setLoader(true);
  };

  function callApi() {
    let latitude = props.coordinates.lat;
    let longitude = props.coordinates.lon;
    const apiKey = "23422500afd990f6bd64b60f46cf509a";
    let units = "metric";
    let apiUrl = `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}`;
    axios.get(apiUrl).then(showHourlyForcast);
    console.log("hourly", apiUrl);
  }

  if (loader) {
    return (
      <div className="row">
        <div className="col-md-6">
          <div className="row">
            {hourlyForcastData.map(function (hourlyforcast, index) {
              if (index < 4 && index > 0) {
                return (
                  <div
                    className="col-4 box-weather my-auto text-center"
                    key={index}
                  >
                    <ForcastHour data={hourlyforcast} />
                  </div>
                );
              }
            })}
          </div>
        </div>
        <div className="col-md-6">
          <div className="row">
            {hourlyForcastData.map(function (hourlyforcast, index) {
              if (index < 7 && index > 3) {
                return (
                  <div
                    className="col-4 box-weather my-auto text-center"
                    key={index}
                  >
                    <ForcastHour data={hourlyforcast} />
                  </div>
                );
              }
            })}
          </div>
        </div>
      </div>
    );
  } else {
    // 删掉这里的callApi调用,加载状态直接返回null即可,请求只在useEffect里触发
    return null;
  }
};

export default WeatherHourlyForcast;

核心修改点

  • 删掉了else分支里的callApi()调用,所有API请求只在useEffect里触发,避免渲染阶段意外调用请求
  • 把useEffect的依赖从引用类型props.coordinates改成基础类型props.coordinates.lat和props.coordinates.lon,避免父组件传递的对象引用不稳定导致的不必要触发
  • 调整了useEffect里的执行顺序,先设loader为false再发请求,逻辑更符合预期

内容的提问来源于stack exchange,提问作者Maryam Mohebbi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 14:36:00