hi i am trying to fill array with custom data using multiple apis in react native but unable to do so as nested fetch api giving unpredicatable result my final array is always empty .
Any help would be appreciated
Thanks in advance
below is my code please tell what i am doing wrong here..
fetchSubcategories(category_id){
var url=GLOBAL.BASE_URL+'/categories?filter[where][categoryId]='+GLOBAL.CATEGORY_ID+'&filter[where][category_type]=subcategory&filter[order]=name ASC';
const set = [];
const subcategoriesArray=[];
fetch(url)
.then((response) => response.json())
.then((responseData) => {
for (var i = 0; i < responseData.length; i++) {
set.push(responseData[i]);
}
console.log('response subcategories : '+JSON.stringify(set,null,2));
})
.then(()=>{
for(var j=0;j<set.length;j++){
var name=set[j].name;
var id=set[j].id;
fetch(url)
.then((response) => response.json())
.then((responseData) => {
var subcategory={
name:name,
id:id,
products:[]
}
subcategoriesArray.push(subcategory);
}).done()
}
})
.done()
console.log('final custom josn array : '+JSON.stringify(subcategoriesArray,null,2));
}
Note : urls used above giving the proper result on my end the only thing is final subcategoriesArray should be filled.
fetch is "asynchronous", so your console.log is probably running before the request has actually finished, but the array is actually being populated.
To test: try moving your console.log to just after subcategoriesArray.push(subcategory);
You should see it log out the array for each set, but at least you can see it is actually being populated.
You could wrap the function in a Promise so you can get the array after it has been async populated:
fetchSubcategories(category_id){
return new Promise((resolve, reject) => { // <- add this
var url=GLOBAL.BASE_URL+'/categories?filter[where][categoryId]='+GLOBAL.CATEGORY_ID+'&filter[where][category_type]=subcategory&filter[order]=name ASC';
const set = [];
const subcategoriesArray=[];
fetch(url)
.then((response) => response.json())
.then((responseData) => {
for (var i = 0; i < responseData.length; i++) {
set.push(responseData[i]);
}
console.log('response subcategories : '+JSON.stringify(set,null,2));
})
.then(()=>{
for(var j=0;j<set.length;j++){
var name=set[j].name;
var id=set[j].id;
fetch(url)
.then((response) => response.json())
.then((responseData) => {
var subcategory={
name:name,
id:id,
products:[]
}
subcategoriesArray.push(subcategory);
if(j == set.length-1) { // <- add this block
resolve(subcategoriesArray);
}
}).done();
}
}).done()
}); // <- completes promise block
}
You can then use it like this:
fetchSubcategories(category_id).then((subCatArray) => {
console.log('final custom join array', subCatArray);
});
Hope this helps! :)
Related
I am trying to get my current location in react native, using react-native-geolocation I get latitude and longitude of my location. Now I want to convert them into the location's address without using the Google API key.
Is there any way to convert latitude longitude into an address without using the Google API key?
There are many ways to convert lon/lat to address without using Google Maps API. Search reverse geocoding api and you'll find a bunch of alternatives.
A few months ago I was being overcharged by Google for reverse geocoding API requests. So I switched to Here. They have a free tier that offers 250k requests/months, which works for my app. See the docs here: https://developer.here.com/documentation/examples/rest/geocoder/reverse-geocode
This will give you highly detailed address data (unlike ip-api.com suggested by Muhammad).
Here is the wrapper function I use to call the API:
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
// the response had a deeply nested structure :/
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
resolve()
}
})
.catch((e) => {
console.log('Error in getAddressFromCoordinates', e)
resolve()
})
})
}
there are many alternatives you can search reverse geocoding API
Solution 1:
By using Google map key
const myApiKey="Key Received from Google map"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
fetch(
'https://maps.googleapis.com/maps/api/geocode/json?address=' +
latitude +
',' +
longitude +
'&key=' +
myApiKey,
)
.then(response => response.json())
.then(responseJson => {
if (responseJson.status === 'OK') {
resolve(responseJson?.results?.[0]?.formatted_address);
} else {
reject('not found');
}
})
.catch(error => {
reject(error);
});
});
}
Solution 2:
By using Here Plateform key
https://developer.here.com/documentation/geocoder/dev_guide/topics/example-reverse-geocoding.html
They have a free tier which gives us 250k requests/months free quota
const HERE_API_KEY="Key Received from Here Plateform"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
reject('not found')
}
})
.catch((e) => {
reject(e);
})
})
}
Solution :
const getAddressFromCoordinates = async(latitude, longitude) => {
try {
const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=key`);
const json = await response.json();
setRealAddress(json.results[0]?.formatted_address);
return json?.results[0]
// return json.movies;
} catch (error) {
console.error(error);
}
}
I just ran into the same issue with my react native project and then came here for answers, the first upvoted answer really helped me but I couldn't get it to work on my end so I went to their website and read their docs and it seems they updated the API's response, I was able to get it to work from the updated response and the code is below :
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve, reject) => {
const url = `https://revgeocode.search.hereapi.com/v1/revgeocodeat=${latitude}%2C${longitude}&lang=en-US&apiKey=${HERE_API_KEY}`;
fetch(url)
.then(res => res.json())
.then(resJson => {
if (resJson.items[0].title) {
resolve(resJson.items[0].address.label);
} else {
reject('not found');
}
})
.catch(e => {
reject(e);
});
});
}
Im trying to call an API with fetch from React Native App but itdoesnt log the response data (console.warn('data', data)) for some reason. It prints the 'call to getArtists' log but then nothing happens.
const URL = 'https://jsonplaceholder.typicode.com/posts'
function getArtists(){
console.log('call to getArtists')
return fetch(URL)
.then(response => response.json())
.then(data => {
console.warn('data', data)
})
}
Code is available here: https://snack.expo.io/rkzea2Zlm at components/api-client.js
What am I doing wrong?
First in your "api_client.js", put a return inside like the code bellow.
function getArtists(){
console.log('call to getArtists')
return fetch(URL)
.then(response => response.json())
.then(data => {
return data
})
}
In your App.js just do that inside componentWillMount.
componentDidMount(){
getArtists()
.then(data => {
alert(JSON.stringify(data))
});
}
I have an array of items that I am passing to an API endpoint (using Sequelize as my ORM). I'm trying to iterate over each item and update it, however I'm getting a Unhandled rejection Error: Can't set headers after they are sent.
stepsController.put = (req, res) => {
const { steps } = req.body;
// Steps is an array of objects that I want to update...
steps.map(step => {
Step.findOne({ where: { id: step.id } })
.then(savedStep =>
savedStep
.update({
order: step.order,
})
.then(success => res.status(200).send(success))
.catch(error => res.send(error))
)
.then(ok => res.status(200).send(ok))
.catch(err => res.send(err));
});
};
I believe this is because it's sending the response for each item. Sequelize's update method is a promise. How can I iterate over all of the items and make sure all of the items are updated before sending a single successful response?
There are three ways you can do
Promise.all
Co
Async Await
1) Here it is , you can use Promise.all :
stepsController.put = (req, res) => {
const { steps } = req.body;
// Steps is an array of objects that I want to update...
Promise.all(steps.map(step => {
return Step.findOne({ where: { id: step.id } }).then(savedStep =>
return savedStep.update({
order: step.order,
})
.catch(error => error)
).catch(err => err)
}))
.then(ok => res.status(200).send(ok))
.catch(err => res.send(err));
};
2) Another way is to use co :
const co = require('co');
stepsController.put = co.wrap(function* (req, res) => {
try {
const { steps } = req.body;
// Steps is an array of objects that I want to update...
for(let i=0;i<steps.length ; i++) {
let savedStep = yield Step.findOne({ where: { id: steps[i].id } });
if(savedStep)
yield savedStep.update({ order: steps[i].order});
}
res.status(200).send();
}
catch(err){
res.send(err);
}
});
3) If you’re using Node 8.0+ , there is no need of any package you can directly use async await :
stepsController.put = async(req, res) => {
try {
const { steps } = req.body;
// Steps is an array of objects that I want to update...
for(let i=0;i<steps.length ; i++) {
let savedStep = await Step.findOne({ where: { id: steps[i].id } });
if(savedStep)
await savedStep.update({ order: steps[i].order});
}
res.status(200).send();
}
catch(err){
res.send(err);
}
};
I have this code
export const callingEveSkill = () => (dispatch, getState) => {
fetch('https://esi.tech.ccp.is/latest/characters/' + getState().ViewChr.Cid + '/skills/?datasource=tranquility&token=' + getState().ViewChr.At)
.then(response => response.json())
.then(json => {
var SkillList = ( json.skills.map((item, i) => {
var skill = TypeIdToName(item.skill_id)
return {
skill_id: (skill) ,
current_skill_level: item.current_skill_level,
skillpoints_in_skill: item.skillpoints_in_skill
}
}))
return SkillList
})
.then( SkillList => {
dispatch(updateSk( SkillList))
dispatch(updateSkL('true'))
})
.catch(err => {
console.log("skill error:" + err)
});
}
In side the code i call TypeIdToName to call a 3rd party api to change the skill id to readable text. I see the calls going out and that it returns the readable name but the SkillList show as undefined.
The problem here is that .map() won't wait for Promises to fulfil. here are few thoughts on how to get around that using Promises composing:
1) !IMPORTANT Refactor your API call TypeIdToName() so it returns a Promise
See this for more info: How do I convert an existing callback API to promises?
2) Install Q or any other library that allows Promises combination. (Promise.all might also work for you depending on your env)
https://github.com/kriskowal/q
OR
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
3) When mapping API calls - collect the promises they return.
4) Use Q.all() to return a Promise that will only be fulfilled when all the API calls are done. You might want to limit your pull of simultaneus connections at some point.
So your code would look like this:
import Q from 'q';
export const callingEveSkill = () => (dispatch, getState) => {
fetch('https://esi.tech.ccp.is/latest/characters/' +
getState().ViewChr.Cid +
'/skills/?datasource=tranquility&token=' +
getState().ViewChr.At
)
.then(response => response.json())
.then(json => {
//We build the array of promises here
let promises = json.skills.map((item, i) => {
//If you did the Step 1 - this should return a Promise object
//So our .map() has something to work with
return TypeIdToName(item.skill_id).then(
(skill) => {
//As promises fulfil - the array of promises
//turns into array of objects like this one
return {
skill_id: (skill),
current_skill_level: item.current_skill_level,
skillpoints_in_skill: item.skillpoints_in_skill
}
}
);
})
//And this promises fulfils when all the others do
return Q.all(promises);
})
//So here we get a SkillList
.then(SkillList => {
dispatch(updateSk(SkillList))
dispatch(updateSkL('true'))
})
.catch(err => {
console.log("skill error:" + err)
});
}
How do we implement caching in react-native-video? Basically, when a video is currently streaming from a network resource, how do we save the video somewhere, and then retrieve it when the same resource is access. What is the best approach for this?
The best approach that i would refer you is using react-native-fetch-blob, you can implement it like this:
const RNFetchBlob = require('react-native-fetch-blob').default;
const {
fs
} = RNFetchBlob;
const baseCacheDir = fs.dirs.CacheDir + '/videocache';
//call the downloadVideo function
downloadVideo('http://....',baseCacheDir)
//Function to download a file..
const activeDownloads = {};
function downloadVideo(fromUrl, toFile) {
// use toFile as the key
activeDownloads[toFile] = new Promise((resolve, reject) => {
RNFetchBlob
.config({path: toFile})
.fetch('GET', fromUrl)
.then(res => {
if (Math.floor(res.respInfo.status / 100) !== 2) {
throw new Error('Failed to successfully download video');
}
resolve(toFile);
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err));
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
return activeDownloads[toFile];
}
//To delete a file..
function deleteFile(filePath) {
return fs.stat(filePath)
.then(res => res && res.type === 'file')
.then(exists => exists && fs.unlink(filePath)) //if file exist
.catch((err) => {
// swallow error to always resolve
});
}
Cheers:)