Different behavior using useState and useContext/useReducer

Asked

Viewed 24 times

-2

I have a case that should be very common for you.

I make a query of a sales list in a Rest API and at that time I set the status isLoading to true, so that a Spinner component I have is visible and at the end of the query Seto to false so that it disappears

const [isLoading, setIsLoading] = useState(false)
        
useEffect(() => {
    setIsLoading(true)
    fetchSales()
    setIsLoading(false)
}, [dayInitial, dayFinal])

Well, here begins the problem... Because although I set the value of isLoading to true, it remains false and the query is performed without displaying Spinner. In my research on the internet I found that this behavior is normal, some say that useState does not do this assignment synchronously and so this problem occurs, others claim that the problem occurs because the value does not change within the closure(https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately). Here comes my first inquiry, because I’m starting my studies in React Native. As then you do in this case to enable/disable Spinner?

Continuing... Before this project, I did a similar one, where I looked for a list of clients, but in this project I used the Context API for state management, with the useContext and useReducer Hooks, and the call of the search method was like this:

useEffect(() => {
dispatch({
    type: 'setIsLoading',
    payload: true
})
    
loadClients()

dispatch({
    type: 'setIsLoading',
    payload: false
})

}, [props.route.params?.addValue])

I did basically the same thing, and this time I set up isLoading using useReducer’s Dispatch. In this case I had no problems with asynchronous in the assignment of the state values, I was able to assign the isLoading to true, do the search and finally assign to false. 'Cause in that case it worked?

  • I did not understand why I was negatively marked on this issue. I explained my research, I argued clearly the question and given my research I confirm that it is very useful

2 answers

1

The problem is simple to solve, it seems to me that it was unnecessarily complicated. See the code:

const [isLoading, setIsLoading] = useState(false)
        
useEffect(() => {
    setIsLoading(true)
    fetchSales()
    setIsLoading(false)
}, [dayInitial, dayFinal])

Within the useEffect, you put the isLoading as true, called the function fetchSales and has already put isLoading as false, without waiting for the fetch be executed. With this, React has not even updated the state of the isLoading for true, because it does upgrade in batches.

To solve, just use an asynchronous function:

const [isLoading, setIsLoading] = useState(false)
        
useEffect(() => {
    async function fetchData() {
        setIsLoading(true)
        await fetchSales()
        setIsLoading(false)
    }

    fetchData();
}, [dayInitial, dayFinal])

That way, the state will update, we will await the fetch run and then update the status again.

About your console.log not working, you’ve seen that the useState has an asynchronous behavior and did not understand. In that question this is well answered. In short, what happens is that the value is updated only in the next surrender.

0


After hitting my head all day, I was able to solve by placing an async/await inside the fetchSales method, and inserted the setIsLoading before and after:

const fetchSales = async () => {
        const dateInitial = moment(dayInitial, 'ddd, D MMM YYYY').format('YYYY-MM-DD')
        const dateFinal = moment(dayFinal, 'ddd, D MMM YYYY').format('YYYY-MM-DD')

        try {
            setIsLoading(true)

            console.log(isLoading) //continua dando false, mas o spinner apareceu
                await fetch(server, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ dateInitial, dateFinal })
                })
                    .then(res => res.json())
                    .then(res => {
                        setData(res)
                        setDataMain(res)
                    })
                setIsLoading(false)
            } catch (e) {
                showError(e)
            }
        }

Then in useEffect I call only the method in question:

useEffect(() => {
        fetchSales()
    }, [dayInitial, dayFinal])

The big mystery is that on the console.log isLoading continues to give false, even with Spinner appearing!!!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.