React Native API FETCH Different names for each objects - javascript

I am connecting a REST api from React Native app. I have Json response with filename objects with different names but all the objects have same variables: filename, message, and display.
Number of objects changes with each request to API (REST), the names of objects in response are different depending on requests. But the variables in each object are same as above.
The information I need from this response is only filename text, but it will be acceptable if I get list of objects so I can read through the messages from errors.
The image shows how my objects look like.
This is my fetch request :
const getGists = async () => {
await axios
.get(`https://api.github.com/gists/public?per_page=30`)
.then((r) => {
let n;
for (n = 0; n < 30; n++) {
console.log(r.data[n].files.filename);
// console.log("____________________");
// console.log(r.data[n].owner.avatar_url);
// console.log("____________________");
// console.log(JSON.stringify(r.data[n].files));
}
})
.catch((e) => {
console.log("ERROR", e);
});
};
how is possible to get every filename from these requests even if object name is not the same in each iteration . Thanks for help

Working with the result of the API calls and some higher-order functions, this will work fine:
const getGists = async () => {
await axios
.get(`https://api.github.com/gists/public?per_page=30`)
.then((response) => {
const myDesireResult = response.data.reduce((acc, item) => {
const files = Object.values(item.files);
if (files.length > 1) {
files.forEach((file) => acc.push(file.filename));
} else {
acc.push(files[0].filename);
}
return acc;
}, []);
console.log(myDesireResult);
})
.catch((e) => {
console.log("ERROR", e);
});
};
Explanation:
in the then block, can get the API call result with result.data
with reduce function, looping through the data will start.
since the object in the files has different names, we can get the files with Object.values() easily.
Some of the files contain several items and most of them have just one item. so with checking the length of the file we can do proper action. if the files have more than one element, with another simple lop, we can traverse this file array easily.
Check the working example on codesandbox

Related

Fetch multiple URLs at the same time?

I'm looking for a way to fetch multiple URLs at the same time. As far as I know the API can only retrieve the data I want with a single product lookup so I need to fetch multiple products at once with the url structure "/products/productID/". Note, this is in VUEJS. This is what my code looks like so far:
In my productServices.js:
const productsService = {
getCategory(productID){
const url = `${config.apiRoot}/products/${productID}`
return fetch(url, {
method: 'GET',
headers: {
'content-type': 'application/json',
'Authorization': `Bearer ${authService.getToken()}`
},
})
}
}
In my view:
data() {
return {
featuredProduct: [13,14,15],
productName: [],
productImg: []
}
}
async mounted(){
const response = await productsService.getCategory(this.featuredProduct)
const resJSON = JSON.parse(response._bodyInit)
this.loading = false
this.productName = resJSON.name
this.productImg = resJSON.custom_attributes[0].value
}
So I need to hit all three featuredProduct IDs and store the data. I'm not really sure how to loop through multiple URLS. All of my other API calls have had all the data readily available using search params but for the specific data I need here ( product image ), it can only be seen by calling a single product.
Any help is much appreciated!
Like Ricardo suggested I'd use Promise.all. It takes in an array of promises and resolves the promise it returns, once all the passed ones have finished (it resolves the promises in the form of an array where the results have the same order as the requests).
Docs
Promise.all([
fetch('https://jsonplaceholder.typicode.com/todos/1').then(resp => resp.json()),
fetch('https://jsonplaceholder.typicode.com/todos/2').then(resp => resp.json()),
fetch('https://jsonplaceholder.typicode.com/todos/3').then(resp => resp.json()),
]).then(console.log)
Using map + Promise.all (tested)
Promise.all([1, 2, 3].map(id =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`).then(resp => resp.json())
)).then(console.log);
if you have multiple products in an array which need to be fetched, you could just use:
Code not tested
Promise.all(productIds.map(productId =>
fetch(`https://url/products/${productId}`)
)).then(() => {/* DO STUFF */});
Little suggestion on storing your data:
If you store everything in one array, it makes to whole job way easier. So you could do
fetchFunction().then(results => this.products = results);
/*
this.products would then have a structure something like this:
Array of Obejcts: {
name: "I'm a name",
displayName: "Please display me",
price: 10.4
// And so on
}
*/
Because you have an array of products, I'd start by changing your state names:
data() {
return {
productIds: [13, 14, 15],
productNames: [],
productImages: [],
};
},
Then you can use Promise.all to fetch the products in parallel:
async mounted() {
const responses = await Promise.all(
this.productIds.map(id => productsService.getCategory(id))
);
responses.forEach((response, index) => {
const resJSON = JSON.parse(response._bodyInit);
this.productNames[index] = resJSON.name;
this.productImages[index] = resJSON.custom_attributes[0].value;
});
this.loading = false;
}
You could also consider refactoring getCategory do the parsing for you and return an object containing a name and an image - that way, mounted wouldn't have to know about the internal response structure.
Check the Promise.all method
Maybe you can create the calls that you need by iterating into your data and then request them in bulk.

Why is my Firestore documents' array field always only updated with one less item than the array I send from the front end?

I am trying to update a Firestore document once I save files to Google Cloud Storage. I want to send an array of URLs of the stored files to a document in Firestore as follows...
(attachments: [ {url: "fileone.jpeg", ...}, {url: "filetwo.jpeg", ...}, ...]).
This array of URLs are created at the front end by using firebases getDownloadURL(). I can successfully update my document but the array in the updated document always holds one less item than the array created at the front end. When console.log-ing the array stored in memory (to be sent to firestore) I see that the value within parentheses that preceeds the [{},{}] is one less than array.length as well.
What console logging the fileUrls array (stored in memory to be sent to firestore) shows even though fileUrls.length === 3 and expanding this below line shows all three URL containing objects starting at index 0 :
fileUrls: > (2) [{…}, {…}]
Here is the code from implementing this update process:
let fileUrls = []; // <<-- Array for file urls to be stored
let promises = [];
for (const file of state.post.files) {
const fileRef = storage.ref().child(`my file path`);
const p = fileRef.put(file)
.then(snapshot => {
fileRef.getDownloadURL()
.then(url => {
fileUrls.push({ url: url, type: file.type }) // Adding urls to arr
})
})
promises.push(p);
}
const all = Promise.all(promises);
all
.then(() => {
submissionRef.update({ // <<<--- Updating document created prior.
attachments: fileUrls
})
})
.catch(err => console.log(err));
You are waiting for the file to finish uploading, but you're not waiting until the download URL is fetched. You'll need to involve the promise returned by getDownloadURL into the array of promises passed to Promise.all(). Try returning its promise from the then callback for your file upload:
const p = fileRef.put(file)
.then(snapshot => {
// add a return here to chain the promise returned by getDownloadURL()
// with the promise returned by put()
return fileRef.getDownloadURL()
.then(url => {
fileUrls.push({ url: url, type: file.type }) // Adding urls to arr
})
})
Consider instead using async/await syntax to avoid all these callbacks and make the code easier to read.

JavaScript array not iterable

I hit a really weird issue with a simple JavaScript array last night. I am working on a React Native app powered by Firebase and getting data from the real-time database with the SDK.
Here is the code:
var app = this
this.props.fb.auth().onAuthStateChanged(function(user) {
if (user) {
app.props.fb.database().ref('/users/' + user.uid + "/timeline").once('value').then(function(snapshot) {
var posts = snapshot.val()
return posts
}).then((posts) => {
var statuses = [];
for (i in posts) {
app.props.fb.database().ref('/posts/' + posts[i]).once('value').then(function(snapshot) {
statuses.push(snapshot.val())
});
}
console.log(statuses)
})
}
});
The above code is supposed to get the data from the timeline of each user, iterate through each of the posts on the timeline and then get the data of the post from posts. It then simply pushes the data to the statuses array, which is being console logged at the end.
This is what shows up on the console.
Until the array is expanded, the console doesn't show the items in the array. Additionally, when I try to get the length of the array, it returns 0 and I also can't iterate through the items on the array.
What am I doing wrong?
If posts is an iterable I would look into using some version of q.all and then when all promises are resolved you can reliably log the result of push all those pieces into the statuses array.
some pseudo code:
if (user) {
app.props.fb.database().ref(`/users/${user.uid}/timeline`)
.once('value')
.then(snapshot => snapshot.val())
.then(posts => {
const statuses = [];
const promises = Object.keys(posts).map(key => {
return app.props.fb.database().ref(`/posts/${posts[key]}`).once('value')
.then(snapshot => statuses.push(snapshot.val()));
}
return Promises.all(promises)
})
.then(() => console.log(statuses));
}

Extracting multiple observables into an already mapped stream angular rxjs

Currently building a project in Firebase and Angular 4. I am pulling in an object of data which contains many keys of data, and a lot of different uid keys.
To keep the data somewhat manageable i don't want to save the object data inside some keys where it can be changed at the host.
In the instance i have the uid stored, i want to create a new key of the extracted observable. I can easily add it as an observable inside the mapped object but i want to actually extract the observable into the object during the stream. Here is what i currently have:
this.ss.data()
.map(res => {
res['test1'] = this.fb.serie(res['serieUid']).map(res => res)
return res
})
.subscribe(res => {
console.log(res)
})
Result -
serieUid:"-Kk1FeElvKMFcyrhQP4T"
state:"Tennessee"
test1:FirebaseObjectObservable
timezone:"US/Eastern"
But i would like to have the extracted observable inside 'test1', I have failed on this for a few hours on this and am confused. There is more than one instance of a uid, so this was have to happen multiple times. Then subscribe.
Follow up after answered below:
Heres what i ended up with in my final function
getRound(roundUid: string) {
/**
Retrieves a round based simply on the uid
supplied.
**/
return this.fb.round(roundUid)
.mergeMap(round => // Get Serie Data
this.fb.serie(round['serieUid']).map(serie => {round['serie'] = this.cs.cleanForFb(serie); return round})
)
.mergeMap(round => // Get Type Data
this.fb.type(round['typeUid']).map(type => {round['type'] = this.cs.cleanForFb(type); return round})
)
.mergeMap(round => // Get sx Coast Data
this.fb.coast(round['sxCoastUid']).map(sxCoast => {round['sxCoast'] = this.cs.cleanForFb(sxCoast); return round})
)
}
Try this. The mergeMap() operator subscribes to the internal Observable whose result is using map() turned into the original res updated with test1.
this.ss.data()
.mergeMap(res => this.fb.serie(res['serieUid'])
.map(innerRes => {
res['test1'] = innerRes;
return res;
})
)
.subscribe(res => {
console.log(res)
})

Firebase storage failing silently?

I'm trying to get the download url for multiple images, then trigger a change in my app. But... if one of those images doesn't exist for whatever reason, everything fails silently.
Here's the code:
const promises = [];
snapshot.forEach(childSnapshot => {
const child = childSnapshot.val();
const promise = firebase.storage()
.ref(child.songImagePath)
.getDownloadURL()
.catch(err => {
console.log('caught', err);
return "";
})
.then(imageURL => {
return imageURL;
});
promises.push(promise);
});
Promise.all(promises)
.catch(err => {
console.log('caught', err);
})
.then(urls => {
...do something with urls array
});
I'm using child.songImagePath in my database to store the image's location in storage. If ALL paths for ALL images have images, everything works perfectly.
BUT if an upload went awry or for some reason there's no image in the storage location, it fails silently. None of my catches fire. And Promise.all is never resolved.
What's going on here? Is there a way to check for a file's existence before calling getDownloadURL?
EDIT: As #mjr points out, in the documentation they've formatted their error callback slightly differently than I have. This also seems to never fire an error, though:
.then(
imageURL => {
return imageURL;
},
err => {
console.log('caught', err);
return "";
}
);
Firebase Storage JS dev here.
I ran your code with minor changes[1] in Chrome and React Native, and didn't see that behavior.
I see Promise.all always resolving (never failing), with an empty string in the array for invalid files. This is because your .catch handler for getDownloadURL returns an empty string.
For further troubleshooting, it would be useful to know:
version of the firebase JS library you are using
the browser/environment and version
network logs, for example from the network panel in Chrome's dev tools, or similar for other browsers
The firebase-talk Google Group tends to be a better place for open-ended troubleshooting with more back-and-forth.
[1] For reference, here's my code:
const promises = [];
// Swap out the array to test different scenarios
// None of the files exist.
//const arr = ['nofile1', 'nofile2', 'nofile3'];
// All of the files exist.
const arr = ['legitfile1', 'legitfile2', 'legitfile3'];
// Some, but not all, of the files exist.
//const arr = ['legitfile1', 'nofile2', 'nofile3'];
arr.forEach(val => {
  const promise = firebase.storage()
    .ref(val)
    .getDownloadURL()
    .catch(err => {
// This runs for nonexistent files
      console.log('caught', err);
      return "";
    })
    .then(imageURL => {
// This runs for existing files
      return imageURL;
    });
  promises.push(promise);
});
Promise.all(promises)
  .catch(err => {
// This never runs
    console.log('caught', err);
  })
  .then(urls => {
// This always runs
    console.log('urls', urls);
  });

Categories

Resources