Reputation: 133
I have a feedItems
array. I want to set this array's items according to axios
get request's response. In the screen, I want to show the information of the elements of this array. So, before everything, I must set this array so that I will be able to show the info of elements of this array.
I have no problem while making an API request and I am sure that the response.data
is not empty. However, when I use the setFeedItems(...feedItems, response.data[i]);
function, I get the following error.
[Unhandled promise rejection: TypeError: Invalid attempt to spread non-iterable instance.]
Here is the code:
const [feedItems, setFeedItems] = useState([{}]);
useEffect(async () => {
console.log("here");
let accessToken = await AsyncStorage.getItem("accessToken");
const response = await axios
.get(
"http://repor****-env-1.eba-nj*******/feed",
{
headers: {
Authorization: "Bearer " + accessToken,
},
}
)
.then((response) => {
for (let i = 0; i < response.data.length; i++) {
setFeedItems(...feedItems, response.data[i]);
}
//console.log(feedArray);
console.log(feedItems);
});
}, []);
Upvotes: 1
Views: 965
Reputation: 1074148
The problem is that you're spreading out [{}]
into discrete arguments to setFeedItems
. To append response.data
to feeditems
, you do this:
.then((response) => {
setFeedItems(feedItems => [...feedItems, ...response.data]);
// No `console.log(feedItems)` here, it will show you outdated information
});
Notice:
[]
around the return value so you're creating an array and spreading the items out into it.feedItems
and the new response.data
out into that new array.But if you want to replace feedItems
with the data from response.data
(which it looks like you probably do in this specific case), it's simpler:
.then((response) => {
setFeedItems(response.data);
// No `console.log(feedItems)` here, it will show you outdated information
});
Also, in the normal case, your feedItems
would start out with an empty array, not an array with an empty object in it. So:
const [feedItems, setFeedItems] = useState([]);
Separately: useEffect
won't do anything useful with the promise an async
function returns (and does look at the return value, to see if it's a function), so you shouldn't pass an async
function into it. Since you're already using explicit promise callbacks, there's no reason for the await
and the async
function. Also, you should handle promise rejection.
Putting all of that together (using the "replace" rather than "append" option):
const [feedItems, setFeedItems] = useState([{}]);
useEffect(() => {
AsyncStorage.getItem("accessToken").
then(accessToken => axios.get(
"http://repor****-env-1.eba-nj*******/feed",
{
headers: {
Authorization: "Bearer " + accessToken,
},
}))
.then((response) => {
setFeedItems(feedItems => [...feedItems, ...response.data]);
})
.catch(error => {
// ...handle/report the fact an error occurred...
});
}, []);
Live Example:
const { useState, useEffect } = React;
// Mock `AsyncStorage`
const AsyncStorage = {
getItem() {
return Promise.resolve("some token");
},
};
// Mock `axios`
const axios = {
get() {
return new Promise(resolve => {
setTimeout(() => {
resolve({data: [
{id: 1, text: "Item 1"},
{id: 2, text: "Item 2"},
{id: 3, text: "Item 3"},
{id: 4, text: "Item 4"},
]});
}, 800);
});
},
};
const Example = () => {
const [feedItems, setFeedItems] = useState([]);
useEffect(() => {
AsyncStorage.getItem("accessToken").
then(accessToken => axios.get(
"http://repor****-env-1.eba-nj*******/feed",
{
headers: {
Authorization: "Bearer " + accessToken,
},
}))
.then((response) => {
setFeedItems(response.data);
})
.catch(error => {
// ...handle/report the fact an error occurred...
});
}, []);
return <div>
<div>Item count: {feedItems.length}</div>
{feedItems.map(({id, text}) => <div key={id}>{text}</div>)}
</div>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Upvotes: 4
Reputation: 1447
Why not updating the state with all the items inside response.data
at once?
Instead of
for (let i = 0; i < response.data.length; i++) {
setFeedItems(...feedItems, response.data[i]);
}
just do:
if (response.data.length) setFeedItems(prevState => [...prevState, ...response.data])
Upvotes: 1
Reputation: 14171
OK, there are a couple of issues with your code:
useEffect
shouldn't use async
functions. You should create an async function inside and call it there.await
and then
. Should stick to one.setFeedItems(...feedItems, response.data[i]);
instead of an array.Try to use this
setFeedItems([...feedItems, response.data[i]]);
This will fix the error, but it won't fix your problem, as you are getting old feedItem
.
Upvotes: 3