react Invalid attempt to spread non-iterable instance - reactjs

I keep getting this error:
TypeError: Invalid attempt to spread non-iterable instance
while I'm trying to fetch some data in here :
export const genres = () => {
const apiUrl = "http://localhost:3000/api";
return fetch(apiUrl + "/genres")
.then(response => response.json())
.then(data => {
const res = Array.from(data.results);
return res;
});
};
console.log(genres)
export function getGenres() {
return genres().then(res => res.filter(g => g));
}
and updating the state of my component in here :
componentDidMount() {
const genres = [{ _id: "", name: "All Genres" }, ...getGenres()];
this.setState({ genres});
}
I'm aware that probleme comes from the fact that genres returns an object while the state should be an array but I'm not sure how to fixe it.
Thanks

getGenres returns a promise, so you need to wait for it to resolve before you try to put what is returned from it in state.
componentDidMount() {
getGenres().then(res => {
const genres = [{ _id: "", name: "All Genres" }, ...res];
this.setState({ genres });
})
}

Related

React does not rerender on updated state of nested array

I have an array of objects like so:
const [categories, setCategories] = React.useState([
{
id: 1,
title: 'Top Picks',
subTitle: "Today's hottest stuff",
images: [],
searchQuery: 'shoes',
},
...]);
Which I update with values in useEffect once like so:
React.useEffect(() => {
const newCategories = categories.map(category => {
fetch(`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`, {
headers: {
'Authorization': apiKey,
},
}).then(r => {
r.json().then(convertedJson => {
category.images = convertedJson.photos;
});
});
return category;
});
setCategories(newCategories);
}, []);
however the child components here never rerender and I cannot figure out why. My understanding is that .map creates a new array anyhow, so the spread syntax isn't necessary in setCategories() but regardless it does not work.
{categories.map((category, i) => (
<CategorySlider {...category} key={i}/>
))}
There's a few issues but the primary issue I see is you're returning the category before the fetch can complete - so even when those fetch calls inside your map complete, you already returned the category below before the fetch completes.
Try using the .finally() block:
React.useEffect(() => {
const newCategories = categories.map(category => {
const c = {...category}; // <--- good practice
fetch(`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`, {
headers: {
'Authorization': apiKey,
},
}).then(r => {
r.json().then(convertedJson => {
category.images = convertedJson.photos;
});
}).catch((err) => {
console.error(err);
}).finally(() => {
return category;
});
});
setCategories(newCategories);
}, []);
Thanks! Using setState before the promises resolved was indeed the problem. The solution looks like this now:
React.useEffect(() => {
async function fetchImages() {
const promises = categories.map(async category => {
const response = await fetch(
`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`,
{
headers: {
Authorization: apiKey,
},
}
);
const convertedJson = await response.json();
category.images = convertedJson.photos;
return category;
});
setCategories(await Promise.all(promises));
}
fetchImages();
}, []);

How to fetch one document from Firebase and how to pass the id to delete it?

this is my react native + firebase project and i have got 2 questions:
How do you suggest to pass the id from one CV ?
How do i fetch only one CV from firebase, cause if i try this it gives me this error:
TypeError: undefined is not an object (evaluating 'querySnapshot.docs.map')]
fetching all the documents from the collection is fine
getCv: () => {
const id = "eccc137b-88be-470d-a0b8-c90b58a6473a"
return firebase
.firestore()
.collection('cvs')
.doc(id)
.get()
.then(function(querySnapshot) {
let cvs = querySnapshot.docs.map(doc => doc.data())
// console.log(doc.data())
return cvs
})
.catch(function(error) {
console.log('Error getting documents: ', error)
})
}
This is my fetchCV method
fetchCvs = async () => {
try {
const cvs = await this.props.firebase.getCv()
//const cvs = await this.props.firebase.getCvs()
//console.log(cvs)
this.setState({ DATA: cvs, isRefreshing: false })
} catch (e) {
console.error(e)
}
}
This is how i add one CV
onSubmit = async () => {
try {
const cv = {
photo: this.state.image,
title: this.state.title,
description: this.state.description,
salary: this.state.salary,
createdAt: new Date().toISOString()
}
this.props.firebase.uploadCv(cv)
this.setState({
image: null,
title: '',
description: '',
salary: '',
createdAt: ''
})
} catch (e) {
console.error(e)
}
}
uploadCv: cv => {
const id = uuid.v4()
const uploadData = {
id: id,
cvPhoto: cv.photo,
cvTitle: cv.title,
cvDescription: cv.description,
cvSalary: cv.salary,
cvCreatedAt: cv.createdAt
}
return firebase
.firestore()
.collection('cvs')
.doc(id)
.set(uploadData)
},
and This is how i implemented the deleteCv method
onDelete = async () => {
const cvId = {
id: this.state.title
}
//this.props.firebase.deleteItem(cv);
const deleteId = this.props.firebase.deleteItem(cv);
console.log(deleteId)
}
I have different error, when I try similar code in nodejs, but I think its the same reason. In line:
let cvs = querySnapshot.docs.map(doc => doc.data())
As you are using get on DocumentReference querySnapshot is instance of DocumentSnapshot which does not have property docs. I think you should use querySnapshot.data() first and than manipulate on data returned.
Or maybe you wanted to use get on collection, not on document, and than you will get QuerySnapshot object and .doc array will be available.

Autorefresh function passing id from Url

I want to have autorefresh function while passing id from the URL
componentDidMount() {
this.interval = setInterval( this.props.fetchId({ id: this.props.match.params.id }), 15000)
this.props.fetchId({ id: this.props.match.params.id })
}
Implementation of fetchId in actions/index.js
export const FETCH_ID = 'fetch_id'
export const fetchId = (params) => async (dispatch, getState, api) => {
const res = await api.get('/api/records/' + params.id)
dispatch({
type: FETCH_ID,
payload: { id: params.id, data: res.data }
})
}
I tried with above code ,it works fine with initial load but after the component is rendered ,this.props.match.params.id is getting undefined and getting "Uncaught SyntaxError: Unexpected identifier"
I found answer for this question,I was invoking function instead of calling it.When I called the function like below, auto refresh started working as expected
componentDidMount() {
this.interval = setInterval( () => this.props.fetchId({ id: this.props.match.params.id }), 15000)
this.props.fetchId({ id: this.props.match.params.id })
}

Setting state object dynamically using the data returned using Promise.all and fetch API : React+Typescript

I am using fetch API and promise.all for a scenario where I am passing an array of URL'S from where I am fetching the data. The data retrieved from all the above URL'S needs to be set to the state object.
Say I have an array of 5 URL's , the result returned by these must be
assigned to the 5 different values inside my state object.
Using React along with typescript.
Help would be appreciated.
This is what I have tried so far
import * as React from 'react';
const urls = [ 'http://localhost:3001/url1',
'http://localhost:3001/url2',
'http://localhost:3001/url3',
]
interface IState {
test: [],
result: [],
returnVal: []
}
export default class App extends React.Component<{},IState> {
constructor(props:any)
{
super(props);
this.state = {
test: [],
result: [],
returnVal: []
}
checkStatus(response:any) {
if (response.ok) {
return Promise.resolve(response);
} else {
return Promise.reject(new Error(response.statusText));
}
}
parseJSON(response:any) {
return response.json();
}
setData(data:any){
Object.entries(this.state).forEach(([key], index) => {
this.setState({ [key]: data[index] })
});
}
componentDidMount()
{
Promise.all(urls.map(url =>
fetch(url)
.then(this.checkStatus)
.then(this.parseJSON)
.catch(error => console.log('There was a problem!', error))
))
.then(data => {
this.setData(data);
})
}
render() {
return(
//some rendering code
)
}
}
Need to set the data returned from promise to the state object variables.
Promise.all(urls.map(url =>
fetch(url)
.then(this.checkStatus)
.then(this.parseJSON)
))
.then(jsons => {
var newState = {};
var index = 0;
for(var key in this.state)
newState[key] = jsons[index++];
this.setState(newState);
})

How to use axios.all or promise.all to call all ajax calls just once? React

I have some AJAX calls using axios, and I want to change the state of all only when the last axios call is finished, I tried to use axios.all but I could not, could anyone help me?
The call:
export default class TeamStatus extends React.Component {
state = {
updated2018: "",
updated2017: "",
updated2016: "",
totalSkills: "",
totalNotUpdated: "",
}
componentWillReceiveProps(props) {
const firstName = localStorage.getItem('nameLoggedUser');
const lastName = localStorage.getItem('lastNameLoggedUser');
const fullName = `${firstName} ${lastName}`.toLowerCase();
const loggedUserIs = localStorage.getItem("user-role");
if (loggedUserIs === 'full') {
axios.get(`/api/wfmskills/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
totalSkills: res.data.count
})
})
axios.get(`/api/notupdated/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
totalNotUpdated: res.data.count
})
})
axios.get(`/api/updated2017/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2017: res.data.count
})
})
axios.get(`/api/updated2016/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2016: res.data.count
})
})
axios.get(`/api/updated2018/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2018: res.data.count,
});
})
}
}
So, i have 5 calls, and i want to change the state (update2018,2017,2016,totalSkills and notUpdate) when the last to finish (api/update2018)
Someone could help me? PLEASE???
Here is a quick way to wrap all your promises in 1 promise all, and then when all of them complete, it will trigger 1 setState. This will allow you to execute code after all 5 are done completing.
export default class TeamStatus extends React.Component {
state = {
updated2018: "",
updated2017: "",
updated2016: "",
totalSkills: "",
totalNotUpdated: "",
}
componentWillReceiveProps(props) {
const firstName = localStorage.getItem('nameLoggedUser');
const lastName = localStorage.getItem('lastNameLoggedUser');
const fullName = `${firstName} ${lastName}`.toLowerCase();
const loggedUserIs = localStorage.getItem("user-role");
if (loggedUserIs === 'full') {
const { managerStatusFiltered, cityStatusFiltered, countryStatusFiltered, squadNameStatusFiltered }
Promise.all([
axios.get(`/example1`),
axios.get(`/example2`),
axios.get(`/example3`),
axios.get(`/example4`),
axios.get(`/example5`),
]).then(([res1, res2, res3, res4, res5]) => {
this.setState({
totalSkills: res1.data.count,
totalNotUpdated: res2.data.count,
updated2017: res3.data.count,
updated2016: res4.data.count,
updated2018: res5.data.count
});
});
}
}
}

Resources