Reputation: 3
So I have Axios request witch takes some specific accounts and I want them to populate my React state array, but it always stays empty
function App() {
const twitchAccounts = ["passhtet", "dallas", "bobross", "riotgames"];
const [live, setLive] = React.useState([]);
React.useEffect(() => {
for (let index = 0; index < twitchAccounts.length; index++) {
axios
.get(
`https://api.twitch.tv/helix/streams?&user_login=${twitchAccounts[index]}`
)
.then((res) => {
if (res.data.data.length > 0) {
return live.push(res.data.data);
}
})
.catch((error) => {
console.log(error);
});
}
}, []);
console.log(live, "live");
return <div />
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<div id="root" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
Upvotes: 0
Views: 1214
Reputation: 13588
In your situation, you should be using Promise.all.
setState is async, and setting state in a loop is not recommended and may produce undesirable behavior (e.g. order of the results may not be the same since axios.get is async and may returned at different timings OR you state may get overwritten by incoming setState when both one or more methods are setting state at the same time)
By using Promise.all, i get all the results in parallel, and I ensure I set the correct results in state.
React.useEffect(() => {
const initialize = async() => {
const results = await Promise.all(twitchAccounts.map(item => {
return axios.get(
`https://api.twitch.tv/helix/streams?&user_login=${item}`
)
.then((res) => {
if (res.data.data.length > 0) {
return res.data.data
}
return null
})
.catch((error) => {
console.log(error);
});
}))
setLive(results)
}
initialize()
}, []);
the live
state is also saved in the same order as your twitchAccounts
Upvotes: 0
Reputation: 642
So you want to send multiple requests and want to populate after all the requests are done. But there are some issues there:
rerenders
after updating the state, which leads to a very slow website.So, the solution can be, wait for each request to end
, push the response in a temporary array
, and after all the requests are done, push the whole array in the state
. That will update the state once.
So here is a useEffect
that will help you
React.useEffect(() => {
(async() => {
const data = await Promise.all(twitchAccounts.map(item => {
axios.get(
`https://api.twitch.tv/helix/streams?&user_login=${item}`
)
.then((res) => {
if (res.data.data.length > 0) {
return res.data.data
}
return null
})
.catch((error) => {
console.log(error);
});
}))
setLive(prev => [...prev, ...data])
})();
}, []);
Upvotes: 0
Reputation: 39
React rules state that when making changes to a state object, you must use setState. In your case, this would be
if (res.data.length > 0) {
return setLive([...live, res.data.data])
}
It is possible that the console.log(live) gets triggered before the api result is received. Try to wrap the console.log(live) in a setTimeout to confirm this.
Upvotes: 1