Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.7k views
in Technique[技术] by (71.8m points)

javascript - React useCallback() and useEffect on event-handler keeps re - rendering

I am learning react from helinski fsopen20. One of the exercises requires to click a button and show the weather of that button bound region.

The button has an onClick event that takes index of regions as the parameter to determine which region is selected (obviously).

<button onClick={() => onClickShow(i)}>{showBox.includes(i) ? myfunction(i): 'show'}</button>

OnClick function then renders details for the said region. The data required to be fetched is being done inside this function

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])
   

I am able to achieve what I want but it costs a lot of re-rendering.

Initially using axios get() WITHOUT useEffect or useCallback resulted in infinite re-render BUT to my surprise,

I have tried useEffect and useCallBack() but nothing is stopping the re-rendering.

On a sidenote, I am using a different useEffect inside my App component which renders once just fine.

How do I properly use useEffect with eventhandler function such as onClick?

Below is the complete code:

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;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is happening because in the dependency array of usecallback, you are passing weather and inside the function, you are setting the state weather. So for the first time, it will be invoked and will set the state i.e weather is updated now which in turn will trigger the usecallback as it will be memoizng a function and whenever value updates of any one of the dependency array elements , it will be invoked.

This will continue likewise. To solve that, either you can remove the weather from the array or can give an empty array from usecallback.But in your case, api call depends on searchResult.So it should be present in the dependency array to trigger the invocation whenever value changes.

const myfunction = useCallback((i)=>{ // the 'i' is passed
    // axios call and return statement
   },[searchResult])  // <<<<<<<<<<<<<<<<<<<< weather has been removed

   useEffect(()=>{

  },[myfunction])
   

For more on usecallback

  1. link1
  2. link2

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...