Showing loading indicator while writing data to Firestore - reactjs

I have a question regarding the fact, that i cant seem to figure out how to show a loader when updating firestore.
I have provided this following example, but dont mind the things i have used in the example. The point is, that I cant seem to figure out how to show a loading indicator until all creations or updates are completely finished.
If i try to setstate before and after the firestore call, the spinner will only appear in 1 second, since the actual call is running in the background (asynchronously i think) when it is updating the firestore.
Can anyone please help me out?
Thanks in advance.
array.forEach(itemInArray => {
db.where(test, '==', itemInArray.test )get()
.then((result) => {
// try to update doc first
db.doc(result.docs[0].id).update({
test: 'test'
});
})
// update successfull
.then(() => console.log('Document exists! We have updated the current one'))
// If the update failed, we create new doc
.catch((err) => {
console.log('Created document);
db.doc().set({
test: 'test'
});
});
});

As we discussed in the chat, if your database operations need to be sequential, you could do something like this:
showSpinner();
updateFirstThing()
.then(() => updateSecondThing())
.then(() => updateThirdThing())
.catch(err => handlerError(err))
.finally(() => hideSpinner());
If some of the operations can be parallel, you'd need to consider using Promise.all to make sure the spinner is hidden when all of the promises are fulfilled (or there was an error).

Related

update state of the parent screen if an update occurs in current screen

I am using react-navigation 6 and react-native CLI, to make a chat application. I want to achieve this feature which is common in every chat application that when we send a message to someone, and go back to homescreen of the app (where all conversations are listed), the last message sent, can be seen.
Like if I sent message and pressed the back button, it will navigate me to home screen where all my conversations are, and it should update the conversation where I sent the message.
I have tried route.params, but it gives a warning that non-serializable values found.
React navigation warning
Also, I have heard that passing setState function to child component is very bad practice as it is mentioned here
I also tried navigation_events along with useEffect , this was a surprise to me that it didn't work either.
When I refresh the screen manually, or logout and log in, then it refreshes completely, but doesn't when I go back from application.
React.useEffect(() => {
navigation.addListener('focus', e => {
fetchConvos();
});
return () => {};
}, []); //also tried [navigation] instead of []
const fetchConvos = () => {
fetch('http://localhost:15000/' + id + '/conversations', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
})
.then(res => res.json())
.then(received => {
if (received?.response !== false) {
setConversations(received);
}
});
};
I have checked the id , received and even setConversations, they all are updating, but my screen still rendering the old messages.
Also, I don't want to use Async Storage or redux for this simple problem like this.
I can share the complete code if this isn't enough.
EDIT
I figured out one more way to update it may help clarify the situation more.
React.useEffect(() => {
navigation.addListener('focus', e => {
setConversations([]); //first setting convos to empty
fetchConvos(); //then fetching new data
});
return () => {};
}, []);
But this method is quite slow as I am updating the state of conversations twice.
I would appreciate if someone can help me here.
By taking the last 2 samples of code, I'd go down the route of setting the state to change the data. Like, I don't know the structure of your code completely. But i'm assuming you're using useEffect inside some component, right? In that case, React Context might be what you're looking for:
How to use react hooks on react-native with react-navigation
It allows to share informations, without having to build a structured store like redux. You should probably working on redesign a bit the code as, if you're following the current logic, you're going to split data pool of the conversation in the menu and load them when the "back" navigation event occurs, right?
Whilst the conversation data should be shared and available to both components, regardless where you're.
At least I'd rethink it this way to allow consistent data throughout the whole application.
Unless you've to do something specific and on-spot, of course.

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.

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

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).

How to fix First Character Only bug

I am facing a complex bug where two json fields, the name and the description, only show their first character on a table every now and then randomly for no apparent reason. Has anyone ran into this issue? Attached screenshots of behavior below.
My web stack consists of a golang api get request being called using axios by a react frontend where I feed a react table component.
Table with first character only bug
Table with expected result
Axios Call
fetchMaintenanceEventsData() {
let parent = this;
console.log('Fetching Maintenance Events Data');
this.setState({
maintenanceEventsData: [],
maintenanceEventsDataLoading: true
});
axios.get(`server-status/events`)
.then((res) => {
// console.log('res.data: ', res.data);
parent.setState({
maintenanceEventsData: res.data,
maintenanceEventsDataLoading: false
});
})
.catch((error) => {
console.log("Error:", error);
parent.setState({
maintenanceEventsDataLoading: false
})
});
}
I expect the table fields to display completely 100% of the time.

How to have a server return a remote json object to client? react/express/web3

I'm using web3 to send a transaction to Ethereum. My express server logs the block, transactionHash, etc. data as a json object. I need the json returned to the client.
This question is running the risk of repeating a previous question, but I believe that it is more refined, to the point, and ultimately a different question. These previous threads have helped me remove several errors from the code and zero in on what is actually happening.
How to return json data to a react state?
How to await a json return value (the return takes at least 30 seconds) before logging it? javascript/react/express
How to set state of a react component with a specific item from a returned json object?
The specific code that is returning a blank value instead of the json object is:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", console.log, res.json());
Client Code:
axios
.post("http://ec2-54-67-28-69.us-west-1.compute.amazonaws.com:3000/")
.then(response => console.log(response.data, payment))
.catch(function(error) {
console.log(error);
})
I somehow need to get the json object inside the res.json() but putting it inside the function () does not work. Any suggestions?
Thanks!
Edit. I am pretty sure I need to use something from this:
web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})
.once('transactionHash', function(hash){ ... })
.once('receipt', function(receipt){ ... })
.on('confirmation', function(confNumber, receipt){ ... })
.on('error', function(error){ ... })
.then(function(receipt){
// will be fired once the receipt is mined
});
Try this:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", res.json);
The way you did it res.json() was called without parameters.

Resources