How to wait for getDownloadURL to finish in my mapping function before updating my object array in react state? - reactjs

getImages() {
const entries_copy = this.state.entries;
entries_copy.map(entry => {
storage.refFromURL(entry.sign_in_photo).getDownloadURL()
.then((url) => {
entry["inPhotoURL"] = url;
storage.refFromURL(entry.sign_out_photo).getDownloadURL()
.then((url) => {
entry["outPhotoURL"] = url;
});
}).catch((error) => {
// Handle any errors
});
});
this.setState({entries: entries_copy});
}
I'm trying to retrieve the download url for images and store them in my entry object inside my entries object array but the problem I'm facing right now is that the setState is called before the urls are retrieved and I have no idea how to wait for it to complete before setting the state. I have searched for similar problems but most of them are solved by executing it inside then() but for mine, I can't execute it inside then() because I have to wait for all the entries to be updated. I have only recently started using React for this project so I'm sorry if the answer is obvious.

This is because the code in asynchronous.
You should call setState inside the .then() function.
I would recommend you to read about Promises in Javascript. They are an important aspect of the language to master.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

In addition to the answer of #TomSlutsky, note that you need to correctly chain your promises and you should not forget to "always return results, otherwise callbacks won't catch the result of a previous promise".
So you need to do as follows:
storage.refFromURL(entry.sign_in_photo).getDownloadURL()
.then((url) => {
entry["inPhotoURL"] = url;
return storage.refFromURL(entry.sign_out_photo).getDownloadURL()
})
.then((url) => {
entry["outPhotoURL"] = url;
this.setState(...);
})
.catch((error) => {
// Handle any errors
});
Note also how the catch() method is called at the end of the chain, see the doc for more details (and possible other options).

Related

Spread Operator not copying results in React

I am trying to update setState in a for loop, but for some reason state isn't being copied it's just being replaced. There should be 2 clients, instead I am getting one. Can anyone tell me why this is happening? The console.log is returning both clients.
const handleViewClients = () => {
for (let i = 0; i < clients.length; i++) {
console.log(clients[i].clientid);
fetch("http://localhost:3005/all-clients/" + clients[i].clientid)
.then((response) => response.json())
.then((result) => {
console.log(result);
setBarbersClient({
...barbersClient,
client: result,
});
});
}
};
I have also tried this... The console.log is returning what I need
Promise.all(
clients.map((client) =>
fetch("http://localhost:3005/all-clients/" + client.clientid)
)
)
.then((resp) => resp.json())
.then((result) => {
console.log(result.username)
setBarbersClient({
...barbersClient,
client: result,
});
});
Here is the route from the server side
app.get("/all-clients/:clientid", (req, res) => {
db.NewClientsx.findOne({
where: {
id: req.params.clientid,
},
}).then((response) => {
res.json(response);
});
});
There some fundamental concepts of sync vs. async code that you aren't accounting for here. State changing (and fetching) is asynchronous, so it won't run until after this synchronous loop has finished being executed (during which the state value will remain unchanged). Also, it's a bad idea to change state in a loop, for this reason and others.
Fetch all the clients, then do one state change at the end with all the fetched data. You can utilise things like Promise.all and Promise.spread to achieve this. Here's an example of doing multiple fetches then dealing with the results in one batch: How can I fetch an array of URLs with Promise.all?
You're making two distinct mistakes of which either is enough to cause the behaviour you're seeing.
1. You're overwriting the client property.
Every time you call the setter function you're overwriting the previous value of the client property. You'll need some data structure that supports multiple values like a map:
setBarbersClient({
...barbersClient,
clients: {
...barbersClient.clients,
[result.id]: result
},
});
You will need to change your render logic somewhat to accomodate the new data structure.
2. You're using a stale reference.
When you access barbersClient its setter may have already been called with a different value and your reference to it still refers to the value of the previous run of the render function. You can make sure your reference is fresh by using a set state action callback.
setBarbersClient(previousValue => {
...previousValue,
clients: {
...previousValue.clients,
[result.id]: result
},
});
previousValue will never be stale inside the set state action function body.

Where is fetched data saved in React?

I am trying to understand this snipped of code at an intrinsic level:
fetchAllData(){
fetch('http://ec2-x-x-xx-xx.xx-west-x.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
return res.json();
})
to better understand a simple component like this:
componentDidMount() {
this.fetchAllData();
}
fetchAllData(){
fetch('http://ecx-x-x-xxx-xx.xx-west-x.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
return res.json();
})
.then(resJson => {
this.setState(prevState => {
return{
fetchDataLoaded: true,
fetchData: resJson.data.todolist,
};
});
});
}
When fetching from an API, is the data stored temporarily in the res
=> function and chained on using .then?
If so, how could I visualise (in the console maybe?) the properties of the data fetched?
I find myself in a position where I need to manipulate data pulled from an API I don't know the shape of.
I am new to React and any detailed explanation would help a lot, thank you.
This isn't a react thing at all, but rather plain javascript and promises. fetch returns a resolved promise. The response isn't "saved" in res, per se, but rather is passed to a function where you've named the parameter res. If you want to view the raw response res you can do that in the first chained then, ensuring you still return the json promise for the next thenable.
fetch('http://ec2-3-8-196-93.eu-west-2.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
console.log('res', res);
return res.json();
})
Perhaps it would be a little clearer broken down a bit. Factor out the anonymous inline function into a named one, and pass that as the thenable callback. The fetch result isn't really saved anywhere (it is technically in memory in the browser heap, but that's another topic) and is just being passed to a function.
const logResultAndReturnJson = result => {
console.log('result', result);
return result.json();
};
fetch('http://ec2-3-8-196-93.eu-west-2.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(logResultAndReturnJson)
If you need to manipulate the fetched data, then you likely want to look at the resolved JSON object and not the response object.
In the given example, the variable resJson contains the response body parsed by JSON(i.e. this piece of code only works if the API returns a JSON response).
Adding on to #drew, This .then(...).then(...) is called Promise Chaining. It is a useful way of making a flow where you can process data in stages and then deal with errors in the end.
As Reference, these two pages will surely help
promise-basics
promise-chaining

Exporting an array within an ".then" doesnt work

I'm new to NodeJS and are only familiar with Java. I'm trying to create a file that creates objects based on a database and adds them to an array. This array I want to be able to export so that I can use it throughout the whole program, but when I try to export the array it doesn't work. I've tried googling and understanding but haven't come across anything that was helpful unfortunately.
I hope that someone can help me understand
I've tried calling module.exports after the ".then" call, but it just returns an empty array because its async.
I've also tried calling module.exports = teams inside the .then call but it didn't work neither.
var teams = [];
function assignTeamsToClasses() {
return new Promise((resolve, reject) => {
getAllTeamsInDb((teamList) => {
teamList.forEach((aTeam) => {
let newTeam = new Team(aTeam['teamid'], aTeam['teamname'], aTeam['teamrank']);
teams.push(newTeam);
});
resolve();
});
})
}
assignTeamsToClasses().then(() => {
module.exports = teams;
});
main.js
var teams = require('./initialize.js');
console.log(teams);
I expect it to return all teams that are in the database. I know that array is not empty when called within the ".then" call, but the export part does not.
Simple
the sequence require() + console.log() is synchronous
assignTeamsToClasses() is asynchronous, i.e. it updates teams at some unknown later point in time.
You'll have to design your module API to be asynchronous, e.g. by providing event listener interface or Promise interface that clients can subscribe to, to receive the "database update complete" event.
A proposal:
module.exports = {
completed: new Promise(resolve =>
getAllTeamsInDb(teams => {
const result = [];
teams.each(aTeam =>
result.append(new Team(aTeam.teamid,
aTeam.teamname,
aTeam.teamrank)
)
);
resolve(result);
})
),
};
How to use it:
const dbAPI = require('./initialize.js');
dbAPI
.completed
.then(teams => console.log(teams))
.catch(error => /* handle DB error here? */);
Every caller who uses this API will
either be blocked until the database access has been completed, or
receive result from the already resolved promise and proceed with its then() callback.

Using redux-observable to perform XHR then dispatch success/failure

Moving a large codebase built on redux-thunk over to redux-observable and am struggling to figure out the semantics of an epic that will:
Listen for the LOAD action
Perform an XHR
Dispatch an appropriate SUCCESS or FAILURE action
Here's what I've got so far:
export function editEpic(action$, unusedStore, { xhrClient }) {
return action$
.ofType(LOAD_ACTION)
.map(({ apiParams, operationId }) => {
// xhrClient is an object with methods on it that
// return a Promise of the data
const editOperation = xhrClient[operationId];
// call the api now
return editOperation(apiParams);
})
.map(result => {
return { type: SUCCESS_ACTION, result };
})
.catch(error => {
// We never get here..
return { type: FAILURE_ACTION, error };
});
}
This works just fine when the API call is successful, however in the event that the Promise rejects with an error, the catch operation never gets called.
Could someone point me in the right direction here? We are using:
redux-observable#0.18.0
rxjs#5.5.11
I believe the issue is that that first .map should instead be a .switchMap, .concatMap, or .mergeMap instead. To verify this, stick a log statement in the success case to see what result equals, and i expect you'll see that it's a promise, not the value that the promise resolves to. Furthermore, the promise will be in a pending state; not yet resolved/rejected.
The choice of switch vs concat vs merge will depend on what you want to do if multiple LOAD_ACTION are kicked off in quick succession before the first one can complete. Switch will cancel the first in favor of the new one; concat will wait for the first to complete before moving on to the second; merge will do them in whatever order it can, with no guarantee of the order of the results.

Troubles with Promises

I'm doing an Ionic project and I'm getting a little bit frustrated whit promises and '.then()' although I've read a lot of documentation everywhere.
The case is that I have one provider with the functions loadClients and getWaybills.
The first one gets all the clients that have waybills and the second one gets all the waybills from one concrete client.
loadClients() {
return new Promise(resolve => {
this.http.get('http://localhost/waybills?fields=descr1_sped&idUser='+ this.id)
.map(res => res)
.subscribe(data => {
this.data = data.json();
resolve(this.data);
});
});
}
// GET WAYBILLS
getWaybills(client) {
return new Promise(resolve => {
this.http.get('http://localhost/waybills/?stato=0&idUser='+ this.id +'&descr1_sped='+ client)
.map(res => res)
.subscribe(data => {
this.data = data.json();
resolve(this.data);
});
});
}
On the other hand, on the component welcome.ts I have a function loadWaybills which is called on the view load and is executing the following code, my idea is to get all the clients and then get the respective waybills of each one. Then I'll take just of the ones that are defined.
The problem is that on the second .then() instead of getting the variable data I'm getting just undefined... I've understood that if you put a synchronous code inside .then() can be properly executed and work with the "data" which is the result of the promise. Why am I getting this undefined?
loadWaybills() {
//We first load the clients
this.waybills.loadClients()
.then(data => {
this.waybill = data;
var preClients = this.waybill;
this.clients = [];
//Here we're deleting duplicated clients and getWaybills of them)
for (let i = 0; i < preClients.length; i++) {
if (this.clients.indexOf(preClients[i].descr1_sped) == -1) {
this.waybills.getWaybills(preClients[i].descr1_sped)
.then(data => {
**//Here we'll check if the clients has waybills or not**
this.clientWaybills[i] = data;
this.clients.push(preClients[i].descr1_sped)
});
}
}
});
}
It is hard to say because we don't know what the API is meant to return. For example, there may be a missing field somewhere from the first GET and now for the second one, it returns as undefined sometimes. If it only returns undefined sometimes, a simple solution to this, would be to check that the value is defined before assigning it to the variable.
If it always returns as undefined and shouldn't, try to debug the code and make sure that the values are present before the second .then.

Resources