Home > database >  React endless loop on a get request
React endless loop on a get request

Time:09-05

I have a problem with React, I would like to send a get request to my back-end, which will then return me in my case a boolean.

I would like in the useEffect, to query this, meaning if it is TRUE, something should appear in the console. But it doesn't. I get the following error message:

TypeError: Cannot read properties of undefined (reading 'boolean')

My Code:

const App = () => {
    const [content, setContent] = useState({})

    useEffect(() => {
        console.log("useEffect")

        axios.get("http://localhost:88/api/v1/artwork?token="   token   "&id="   query.get("id")).then(function (res) {
            setContent(res.data)
            if(content.cnt.boolean === true) {
                console.log("success")
            }
        })

    }, [])

    return (
        <div>
            
            {
                content.cnt ? <>
                    <p className='text-white' id='text'>{content.cnt.boolean}</p>
                </> : <></>
            }

        </div>
    )
}

On the page, however, the value true is displayed:

return (
    <div>
        
        {
            content.cnt ? <>
                <p className='text-white' id='text'>{content.cnt.boolean}</p>
            </> : <></>
        }

    </div>
)

So I tried putting "content" in the useEffect below:

useEffect(() => {
    console.log("useEffect")

    axios.get("http://localhost:88/api/v1/artwork?token="   token   "&id="   query.get("id")).then(function (res) {
        setContent(res.data)
        if(content.cnt.boolean === true) {
            console.log("success")
        }
    })

}, [content])

Thereby, the code does what it should, the console returns "success", but in an endless loop.

How can I fix this?

CodePudding user response:

Setting a state does not reflect the value instantly, you need to wait for the next renderer to see the changes, so create another useEffect to listen to all the changes of the content

your code will look something like this

const App = () => {
    const [content, setContent] = useState({})

    useEffect(() => {
        axios.get("http://localhost:88/api/v1/artwork?token="   token   "&id="   query.get("id")).then(function (res) {
            setContent(res.data)
            
        })

    }, [])

    useEffect(() => {
        if(content?.cnt?.boolean) {
            console.log("success")
        }
    }, [content])

    return (
        <div>
            
            {
                content.cnt ? <>
                    <p className='text-white' id='text'>{content.cnt.boolean}</p>
                </> : <></>
            }

        </div>
    )
}

CodePudding user response:

You enter in an endless loop because your useEffect is called whenever the content state changes. In that callback, you perform a new Axios request, which then updates the state named content, which triggers a new re-render. And then you can guess that re-render will call your useEffect, then your Axios request's callback, then setState, then re-render, and here is your endless loop.

React introduced a dependencies mechanism to prevent that, supported by almost every hook. It ensures your callback function will be called only when your dependencies are changed.

Dependencies are just an array of values; whenever this array's values are changed, it will only call your callback.

So, suppose you want to perform the Axios request only once the component is mounted. In that case, you can provide it with an empty array (instead of adding the content value, which changes with every useEffect call), guaranteeing its value never be changed, so your useEffect will be called only once.

useEffect(() => {
    console.log("useEffect")

    axios.get("http://localhost:88/api/v1/artwork?token="   token   "&id="   query.get("id")).then(function (res) {
        setContent(res.data)
        if(content.cnt.boolean === true) {
            console.log("success")
        }
    })

}, [])

CodePudding user response:

Instead of calling that api directly in useEffect. You can put that in function and call that function in useEffect like below:-

const fetchData = () => {
axios.get("http://localhost:88/api/v1/artwork?token="   token   "&id="   query.get("id")).then(function (res) {
            setContent(res.data)
            if(content.cnt.boolean === true) {
                console.log("success")
            }
        })
}

useEffect(() => {
    console.log("useEffect")

    fetchData();

}, [])

CodePudding user response:

In the code piece

  setContent(res.data)
  if(content.cnt.boolean === true) {
    console.log("success")
  }

You are setting your content then checking it immediately, but state updates don't work like that - you must wait until the scope from which the state update is called ends in in order for the update to be executed. There are ways to change this behavior but that isn't required here. While in the same scope, res.data.cnt.boolean exists, but content is still undefined until the state update goes through.

Generally, in an onSuccess function, use what is given to it as input, rather than the external state. If you must use the state, you must use it separately, like Sarkar pointed out in this answer.

  • Related