【问题标题】:React useCallback() and useEffect on event-handler keeps re - renderingReact useCallback() 和 useEffect on event-handler 不断重新渲染
【发布时间】:2021-04-27 12:25:05
【问题描述】:

我正在学习 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;

【问题讨论】:

  • 只需从依赖列表中删除weather,仅此而已。仅供参考,我发布了另一个answer here,其中我建议使用useFn 自定义钩子。使用该钩子代替 useCallback 也可以解决您的问题。

标签: javascript reactjs use-effect usecallback


【解决方案1】:

这是因为在 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. link1
  2. link2

【讨论】:

  • 这不会停止重新渲染,也不会帮助将获取的数据附加到“天气”状态。 'weather' 是一个依赖项,如果不传递给 useCallBack,它将不会被更新
【解决方案2】:

主要问题是您在渲染过程中调用了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。通常,您会将 web-request 移动到事件处理程序或 useEffect 挂钩中,至少在渲染期间不会触发。

【讨论】:

  • 谢谢,现在我明白了这个概念。我处于两难境地,一旦在“myfunction(i)”之外调用 API,因为 API 提取需要从“myfunction()”传入的“i”
  • @ribashsharma 您可以将 API 调用作为您的 onClick 处理程序的一部分。您可以将数据存储在一个状态中(就像您已经在做 setWeather(response.data) 一样)。然后在渲染过程中使用这个状态。这样,API 调用仅因用户交互而发生,而不仅仅是渲染。在设置onClick 事件时,您还可以访问i,因为您正在迭代searchResult。
  • 很抱歉,我无法让它只渲染一次。能否请您给我一小段代码?
猜你喜欢
  • 2021-11-11
  • 1970-01-01
  • 2020-04-01
  • 2022-06-18
  • 2017-12-15
  • 2020-07-06
  • 1970-01-01
  • 2021-09-14
相关资源
最近更新 更多