I was facing a problem, let's say at some point in a react app, I get a list of cities and I have to make individual API calls to fetch info about the cities. I want to update the UI whenever a single request completes.
So the solution that comes to mind first is this:
function MyComponent() {
const [cityList, setCityList] = useState([]);
const [cityData, setCityData] = useState({});
useEffect(() => {
const res = // calling api for city list
setCityList(res.data);
res.data.forEach(city => {
const res = // calling api for `city` data
setCityData({...cityData, [city.id]: res.data});
});
}, []);
return (...) // render UI
}
As you will notice, this code doesn't work. Because the callback passed to useEffect
will always have cityData
from the first render, which is {}
. As a result, cityData
will always have only the last received data.
If we were using class based components, this wouldn't have been a problem. Because this.state
always gives access to the latest state.
What I did is kept the cityData
in a ref. The useRef hook can be used for persisting mutable objects between renders while using hook based components. But updating that ref doesn't trigger a render. So I used a pseudo state to trigger update.
function MyComponent() {
const [cityList, setCityList] = useState([]);
const cityData = useRef({}).current;
const [_, triggerUpdate] = useState(null);
useEffect(() => {
const res = // calling api for city list
setCityList(res.data);
res.data.forEach(city => {
const res = // calling api for `city` data
cityData[city.id] = res.data;
triggerUpdate(Math.random()); // The random thing is probably not necessary
});
}, []);
return (...) // render UI
}
And it works. I'm not sure if it is the best solution. What do you think? See any flaws in the solution? How would you solve this specific problem?
If you liked this post, consider subscribing to my blog. I will notify you via email when I post a new article. I promise never to spam you, or to share your email with any third party.