Reputation: 1342
https://codesandbox.io/s/mow5zl5729
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
function useLoading() {
const [isLoading, setLoading] = React.useState(true);
const [hasError, setError] = React.useState(false);
const loadStuff = aPromise => {
return aPromise
.then(() => {
setLoading(false);
})
.catch(() => {
setLoading(false);
setError(true);
});
};
return { isLoading, hasError, loadStuff };
}
function App() {
const { isLoading, hasError, loadStuff } = useLoading();
useEffect(() => {
loadStuff(axios.get(`https://google.com`));
}, []);
console.log(isLoading, hasError);
return <div />;
}
This is a simplified example of what I mean.
If the promise inside useLoading
is rejected, I would have expected the component to render on mount and then render the second time when the error is caught. So, I expect a total of 2 renders with the following state:
1st render:
2nd render:
Instead, it seems that the component rerenders once after setLoading(false)
and again after setError(true)
. So, I get this:
1st render:
2nd render: ( why? )
3rd render:
I suspect that the issue is somehow my use of the promise inside useEffect
but I'm not sure where my mental model went wrong.
EDIT:
When I change useLoading
to only contain 1 useState
, the problem goes away.
broken:
const [isLoading, setLoading] = React.useState(true);
const [hasError, setError] = React.useState(false);
works:
const [loadingState, setLoadingState] = React.useState({
loading: true,
error: false
});
Upvotes: 2
Views: 2562
Reputation: 55740
Looks like this has something to do with batching of the state updates. As far as I know React based events will trigger the batch updates but not something that gets triggered outside it. promise
in this case.
Because the state calls are not batched, you see the 2nd render
where both are set to false
Upvotes: 2