lyes
lyes

Reputation: 15

UseEffect executes infinitely

I use the useEffect hook to fetch data from database by calling another function getData, when I recieve the data I update the state.

const [employees, setEmployees] = useState([])
const [shownEmployees, setShownEmployees] = useState([])
const [nbrPages, setNbrPages] = useState(0)

const getData = () => {
    axios.request(options).then((response) => {
        setEmployees(response.data)
        setShownEmployees(response.data.slice(0, nbrItems))
        response.data.length%nbrItems > 0 ? setNbrPages((response.data.length/nbrItems)+1) : setNbrPages(response.data.length/nbrItems) 
        console.log(nbrPages)
    }).catch((error) => console.log(error))
    for(let i=0;i<nbrPages;i++){
        pages_temp.push(i)
    }
    console.log(pages_temp.length)
    setPages(pages_temp)
}


useEffect(() => {
    getData()
},[])

The problem nbrPages which is all the time 0, but when I put employees to useEffect as dependency, useEffect is executing many times but this time the nbrPages is bieng calculated. Can anyone explain this behaviour, I've tried every thing. Essentially I want to understand why useEffect is getting executed infinitely when I give it employees as dependency, at what moment employees changes so it triggers the useEffect hook.

Upvotes: 0

Views: 84

Answers (3)

Md Sabbir Alam
Md Sabbir Alam

Reputation: 5054

This is happening because you are updating the employees in the state from the getData method.

setEmployees(response.data)

That is updating the employees and every time the employees are updated the useEffect is being called.

Update: It seems your problem is not with useEffect. You can debug your code and see what is happening at this point,

response.data.length%nbrItems > 0 ? setNbrPages((response.data.length/nbrItems)+1) : setNbrPages(response.data.length/nbrItems)

You can rewrite this part as,

console.log(response.data.length);
console.log(nbrItems);
console.log(response.data.length % nbrItems);

if(response.data.length % nbrItems) {
    let temp = (response.data.length/nbrItems)+1;
    console.log('Temp:', temp); 
    setNbrPages(temp);
} else {
    console.log('Inside else block');
    let temp = response.data.length/nbrItems;
    console.log('Inside else temp:', temp);
    setNbrPages(temp);
}

And see what are the console logs and figure out if your value is alright.

Update2: You can use another useEffect to solve your problem.

const [employees, setEmployees] = useState([])
const [shownEmployees, setShownEmployees] = useState([])
const [nbrPages, setNbrPages] = useState(0)

const getData = () => {
    axios.request(options).then((response) => {
        setEmployees(response.data)
        setShownEmployees(response.data.slice(0, nbrItems))
        response.data.length%nbrItems > 0 ? setNbrPages((response.data.length/nbrItems)+1) : setNbrPages(response.data.length/nbrItems) 
        console.log(nbrPages)
    }).catch((error) => console.log(error))
}


useEffect(() => {
    getData()
},[])

useEffect(() => {
    for(let i=0;i<nbrPages;i++){
        pages_temp.push(i)
    }
    console.log(pages_temp.length)
    setPages(pages_temp)
},[nbrPages]);

Upvotes: 1

Ntshembo Hlongwane
Ntshembo Hlongwane

Reputation: 1051

Key to rendering your useEffect once is applying a cleanup function

SOLUTION:

const [employees, setEmployees] = useState([])
const [shownEmployees, setShownEmployees] = useState([])
const [nbrPages, setNbrPages] = useState(0)
const [mounted, setMounted] = useState(true); //Add this state helping with clean see below in useEffect
const getData = () => {
    axios.request(options).then((response) => {
        setEmployees(response.data)
        setShownEmployees(response.data.slice(0, nbrItems))
        response.data.length%nbrItems > 0 ? setNbrPages((response.data.length/nbrItems)+1) : setNbrPages(response.data.length/nbrItems) 
        console.log(nbrPages)
    }).catch((error) => console.log(error))
    for(let i=0;i<nbrPages;i++){
        pages_temp.push(i)
    }
    console.log(pages_temp.length)
    setPages(pages_temp)
}

useEffect(() => {

    if (mounted){
        getData()
    }

    return ()=>{
        setMounted(false); // Cleanup function so that  function called inside renders once

    }
},[mounted])//useEffect dependency is the state added that is **mounted** 

Debugging your function making api call technique

  1. Avoid using axios create instance for now till you can backtrace your error

  2. Avoid inner logic of saving contents in state for now just console.log your contents and see what is happening

  3. Once you have established 1 and 2 the you know you are on the right track and now you know that error was on your logic then fix from there

     const getData = async() => {
         const {data} = await axios.get(url)
         console.log(data)
    
         //Then if this console.log bring you back data that you want
         //now start rebuilding your logic piece by piece so that if 
         //something bugs out you know that the previous step for sure is
         //not the one cause you have validated it  
     }
    

So if you can take this debugging procedure then you will be on the right track

Upvotes: 0

RowanX
RowanX

Reputation: 1317

  • useEffect when provided a prop/state as a parameter works exactly as componentDidUpdate meaning it would render each time a change happens to this prop/state, in your case you pass employees as a dependency which means it would get executed first and after each axios call the value gets updated that means it would trigger useEffect again and again and so on.
  • Passing an empty array as a parameter works as componentDidMount which means it would get triggered only once at the start.

Upvotes: 0

Related Questions