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

React天气应用中Unsplash API背景图获取问题修复求助

问题修复:React天气应用无限触发API请求及依赖警告问题

问题根源

你的代码中,fetchBackgroundImg和getWeatherType函数会在每次组件渲染时重新创建。如果直接将它们加入useEffect的依赖数组,会导致useEffect反复触发,进而无限调用API;而空依赖数组又会触发React的依赖缺失警告。

修复方案

使用useCallback钩子包裹这两个函数,确保它们的引用在组件生命周期内保持稳定,再将其加入useEffect的依赖数组,即可同时解决无限循环和警告问题。

修改步骤

  • 导入useCallback钩子
  • 用useCallback包裹getWeatherType函数(无依赖,因为它仅依赖传入的参数)
  • 用useCallback包裹fetchBackgroundImg函数(依赖为稳定的变量:apiKey、defaultBackgroundImg、setBackgroundImg)
  • 更新useEffect的依赖数组,加入稳定后的两个函数

修复后的完整代码

import { useState, useEffect, useCallback } from 'react'; 
import axios from 'axios';

import Forecast from "./components/Forecast";
import SearchBar from "./components/SearchBar";
import WeeklyForecast from "./components/WeeklyForecast";
import Footer from "./components/Footer";

import './App.css';

// Generate a temperature from -5 to 40 degrees
const currentTempGenerated = () => Math.floor(Math.random() * 46) - 5;

function App() {

  const [currentLocation, setCurrentLocation] = useState('London'); 

  function updateLocation(newLocation) {
    setCurrentLocation(newLocation);
  }

  // State variable to store the current temperature
  const [currentTemp, setCurrentTemp] = useState(0);

  // Calculate weather type based on current temperature - 用useCallback稳定引用
  const getWeatherType = useCallback((temp) => {
    if (temp >= -5 && temp <= -1) {
      return 'Snow';
    } else if (temp >= 0 && temp <= 3) {
      return 'Fog';
    } else if (temp >= 4 && temp <= 10) {
      return 'Rain';
    } else if (temp >= 11 && temp <= 13) {
      return 'Drizzle';
    } else if (temp >= 14 && temp <= 16) {
      return 'Clouds';
    } else if (temp >= 17 && temp <= 20) {
      return 'Cloudy';
    } else if (temp >= 21 && temp <= 28) {
      return 'Sunny';
    } else if (temp >= 29 && temp <= 31) {
      return 'Thunder';
    } else if (temp >= 32 && temp <= 40) {
      return 'Heat';
    } else {
      return 'n/a';
    }
  }, []); // 无依赖,函数引用稳定

  // State variable to store fetched image URL
  const [backgroundImg, setBackgroundImg] = useState(null); 

  const apiKey = process.env.REACT_APP_UNSPLASH_API_KEY;

  // Default image
  const defaultBackgroundImg =
  'https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80';

  // Fetch image from Unsplash - 用useCallback稳定引用
  const fetchBackgroundImg = useCallback(async (weatherType) => {
    try {
      const response = await axios.get(
        `https://api.unsplash.com/photos/random?query=${weatherType}&client_id=${apiKey}`
      );

      const imageURL = response.data.urls.regular;
      setBackgroundImg(imageURL);
    } catch (error) {
      console.error('Error fetching image from Unsplash.com:', error);
      setBackgroundImg(defaultBackgroundImg);
    }
  }, [apiKey, defaultBackgroundImg, setBackgroundImg]); // 依赖均为稳定值

  useEffect(() => {
    const temp = currentTempGenerated();
    setCurrentTemp(temp);
    const weatherType = getWeatherType(temp);
    fetchBackgroundImg(weatherType);
  }, [getWeatherType, fetchBackgroundImg]); // 加入稳定后的函数依赖


  return (
  <div className='container' style={{ backgroundImage: `url(${backgroundImg || defaultBackgroundImg})`, backgroundSize: "cover", backgroundColor: 'rgba(0, 0, 0, 0.8)'}}>

    <h1 className='logo'>Weatherly</h1>

    <span className='search-forecast'>
      <SearchBar updateLocation={updateLocation} />
      <Forecast currentTemp={currentTemp} location={currentLocation} />
    </span>

    <WeeklyForecast currentTemp={currentTemp}/>
    <Footer/>
  </div>
  );
}

export default App;

说明

  • useCallback会缓存函数的引用,只有当依赖数组中的变量变化时才会重新创建函数
  • 这里的依赖数组中的变量都是稳定的(环境变量、常量、useState返回的 setter 函数),因此函数只会在组件初始化时创建一次
  • useEffect现在只会在组件挂载时执行一次,符合你“仅页面手动刷新时获取一次背景图”的需求,同时消除了依赖警告

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:44:56