如何将API数据设置到React state中并避免无限重渲染问题
问题原因
你将Axios天气请求直接放在了渲染执行的limitSearchArray函数内,每次组件渲染都会触发请求,请求成功调用setWeather修改状态后,又会触发组件重新渲染,进入「渲染→发请求→改状态→重渲染」的无限循环。
解决方法
把天气请求逻辑抽离到useEffect中,仅当需要查询的城市变化时才发起一次请求,具体修改如下:
- 把国家过滤逻辑从渲染函数中抽离,作为
useEffect的依赖项 - 仅当筛选出唯一国家时,才触发一次天气接口请求
- 移除渲染函数中的请求逻辑,避免重复触发状态更新
修改后的完整代码:
import React, { useState, useEffect} from 'react' import axios from 'axios' // 原代码漏了axios导入,这里补全 import './App.css' const api_key = process.env.REACT_APP_API_KEY_WEATHER_API const App = () => { const [countries, setCountries] = useState([]) const [countrySearch, setCountrySearch] = useState('') const [weather, setWeather] = useState( { current:{ temp_c:'', condition:{icon: ''}, wind_mph: '', wind_dir: '' } } ) const hook = () => { axios .get('https://restcountries.eu/rest/v2/all') .then(response => setCountries(response.data)) } useEffect(hook, []) // 抽离国家过滤逻辑,方便作为依赖项 const filteredCountries = countries.filter(country => country.name.toLowerCase().indexOf(countrySearch.toLowerCase()) !== -1 ) // 新增天气请求useEffect,仅依赖项变化时才执行 useEffect(() => { // 仅当筛选出唯一国家时才发起天气请求 if (filteredCountries.length === 1) { const capital = filteredCountries[0].capital axios .get(`http://api.weatherapi.com/v1/current.json?key=${api_key}&q=${capital}&aqi=no`) .then(response => setWeather(response.data)) } }, [filteredCountries, api_key]) const countrySearchHandler = (event) => { setCountrySearch(event.target.value) } const handleShowCountry = (event) => { setCountrySearch(event.target.value) } const limitSearchArray = () => { if (filteredCountries.length === 1) { const country = filteredCountries[0] // 移除了原代码这里的请求逻辑,仅保留渲染逻辑 return ( <div> <h1>{country.name}</h1> <p>capital {country.capital}</p> <p>population {country.population}</p> <h2>languages</h2> <ul> {country.languages.map(language => <li key={language.name}>{language.name}</li>)} </ul> <img src={country.flag} alt={country.name} height='100px'/> <h2>Weather in {country.capital}</h2> <p>temperature: {weather.current.temp_c} Celcius</p> <img src={weather.current.condition.icon} alt={country.capital}/> <p>wind: {weather.current.wind_mph} mph direction {weather.current.wind_dir}</p> </div> ) } else if (filteredCountries.length <= 10){ return ( filteredCountries.map(country => <p key={country.name}>{country.name} <button value={country.name} onClick={handleShowCountry}>show</button> </p>) ) } else { return 'Too many matches, specify another filter' } } return ( <div> find countries <input value={countrySearch} onChange={countrySearchHandler}></input> <div> {limitSearchArray()} </div> </div> ) } export default App
内容的提问来源于stack exchange,提问作者Matthew Everitt
相关产品推荐
相关产品推荐

