我正在从 helinski fsopen20 学习反应。 其中一个练习需要单击一个按钮并显示该按钮绑定区域的天气。
按钮有一个 onClick 事件,它以区域索引作为参数来确定选择哪个区域(显然)。
<button onClick={() => onClickShow(i)}>{showBox.includes(i) ? myfunction(i): 'show'}</button>
OnClick 函数然后呈现所述区域的详细信息。 需要获取的数据是在这个函数内部完成的
const myfunction = useCallback((i)=>{ // the 'i' is passed
axios.get('http://api.weatherstack.com/current'
+ `?access_key=${process.env.REACT_APP_API_KEY}`
+`&query=${searchResult[i].name}`)
.then(response=>{
setWeather(response.data)
})
console.log(weather); //this re-renders to infinity
return[
<h2 key='name'>{searchResult[i].name}</h2>,
<h2 key='sth'>Capital weather: {weather}</h2> // I will beautify this function later
]
},[searchResult, weather])
useEffect(()=>{
},[myfunction])
我能够实现我想要的,但需要大量的重新渲染。
最初使用 axios get() 而没有 useEffect 或 useCallback 导致无限重新渲染但令我惊讶,
我尝试过 useEffect 和 useCallBack() 但没有什么能阻止重新渲染。
顺便说一句,我在我的 App 组件中使用了不同的 useEffect,它呈现一次就好了。
如何正确使用 useEffect 和 onClick 等事件处理函数?
完整代码如下:
import React,{useState, useEffect, useCallback} from 'react'
import axios from 'axios'
const Helper=({searchResult})=>{
const [showBox, setShowBox] = useState([])
const [weather, setWeather] = useState([])
const onClickShow = (index) => setShowBox(showBox.concat(index))
const myfunction = useCallback((i)=>{
axios.get('http://api.weatherstack.com/current'
+ `?access_key=${process.env.REACT_APP_API_KEY}`
+`&query=${searchResult[i].name}`)
.then(response=>{
setWeather(response.data)
})
console.log(weather); /////// INFINTE RE-RENDER
return[
<h2 key='name'>{searchResult[i].name}</h2>,
<p key='capital'>{searchResult[i].capital}</p>,
<p key='popn'>{searchResult[i].population}</p>,
<h3 key='langs'>Languages</h3>,
<ul key='lang'>{searchResult[i].languages.map(lang => <li key={lang.iso639_1}>{lang.name}</li>)}</ul>,
<img key='img' src={searchResult[i].flag} alt="flag" width="100" height="100" object-fit="fill"/>
/* searchResult.map(result=><h2 key={result.population}>{result.name}<br/></h2>),
// searchResult.map(result=><p key={result.population}> {result.capital} <br/> {result.population}</p>),
// <h3 key="id">Languages</h3>,
// searchResult.map(result=> <ul key={result.population}>{result.languages.map(lang => <li key={lang.iso639_1}>{lang.name}</li>)}</ul>),
// searchResult.map(result=><img src={result.flag} alt="flag" width="100" height="100" object-fit="fill" key={result.population}/>)
*/
]
},[searchResult, weather])
useEffect(()=>{
},[myfunction])
if(searchResult.length === 1){
return(
<div>
{myfunction(0)}
</div>
)
}
else{
return(
<>
{
searchResult.length <= 10 ?
searchResult.map((result,i) => <h3 key={result.name}> {result.name}
<button onClick={() => onClickShow(i)}>{showBox.includes(i) ? myfunction(i): 'show'}</button></h3>)
: searchResult
}
</>
)
}
}
const App =()=>{
/// store all countries fetched
const [countries, setCountries] = useState([])
// store each searched country
const [searchName, setSearchName] = useState([])
//store the result country
const [searchResult, setSearchResult] = useState([])
useEffect(()=>{
axios.get('https://restcountries.eu/rest/v2/all')
.then(response=>{
setCountries(response.data)
})
}, [])
const handleSearch = (event) =>{
setSearchName(event.target.value)
if (searchName.length !== 0){
var found = searchName ? countries.filter(country => country.name.toUpperCase().includes(searchName.toUpperCase())) : countries
if(found.length > 10){
setSearchResult("Too many matches, specify another filter")
}
else if(found.length === 1){
setSearchResult(found)
}
else if(found.length === 0){
setSearchResult(found)
}
else{
setSearchResult(found)
}
}
}
return(
<>
<h1>find countries <input value={searchName} onChange={handleSearch} /></h1>
<Helper searchResult={searchResult} />
</>
)
}
export default App;
答案 0 :(得分:0)
发生这种情况是因为在 usecallback 的依赖项数组中,您正在传递 weather
,而在函数内部,您正在设置状态 weather
。因此,第一次,它将被调用并设置状态,即现在更新天气,这反过来将触发 usecallback,因为它将记忆一个函数,并且每当任何一个依赖数组元素的值更新时,它将是调用。
这将同样继续。要解决这个问题,您可以从数组中删除 weather
或可以从 usecallback 中提供一个空数组。但在您的情况下,api 调用取决于 searchResult
。因此它应该存在于依赖项数组中每当值更改时触发调用。
const myfunction = useCallback((i)=>{ // the 'i' is passed
// axios call and return statement
},[searchResult]) // <<<<<<<<<<<<<<<<<<<< weather has been removed
useEffect(()=>{
},[myfunction])
有关 usecallback 的更多信息
答案 1 :(得分:0)
主要问题是您在渲染过程中调用 myfunction
。
const myfunction = useCallback((i) => {
axios.get(
'http://api.weatherstack.com/current'
+ `?access_key=${process.env.REACT_APP_API_KEY}`
+ `&query=${searchResult[i].name}`
)
.then(response=> setWeather(response.data))
这会导致 axios
触发一个请求,如果它获得成功响应,它会设置一个新状态。设置状态会导致重新渲染。此重新渲染再次调用 myfunction
作为其渲染过程的一部分,如果成功则设置新状态。这会导致新的重新渲染。等
您可能不想为每次渲染都调用 API。通常,您会将网络请求移动到事件处理程序或 useEffect
钩子中,至少在呈现期间不会触发。