I am relatively new to React and Firebase. I read several posts about how cumbersome it is to deal with asynchronous functions that return promises or take in callbacks, and there are very few ways I know of for the final output to show “correctly”. I am dealing with many asynchronous methods for my project. From my knowledge, the best place to do async calls as well as put event listeners will be in the componentDidMount
and componentDidUpdate
, hence I have a very light constructor, rendering method and attempt to cramp most of my asynchronous calls there as well as put my realtime database listeners there. Currently these are snippets to how I write the componentDidMount
and componentDidUpdate
code.
async componentDidMount() {
console.log("Component did mount happen");
const channels_value = await f1();
const change_object = {};
change_object[MESSENGER_CHANNELS] = channels_value;
if (this.state[MESSENGER_ACTIVE_CHANNEL_ID]) {
const channel_data = await f2();
const members_value = channel_data["ids"];
const channel_title_value = channel_data["name"];
change_object["ids"] = members_value;
change_object["name"] = channel_title_value;
//Listener for upcoming messages
realtime_db.ref(`${MESSAGES_REALTIME_REFERENCE}/${this.state[MESSENGER_ACTIVE_CHANNEL_ID]}`)
.orderByChild(MESSAGE_CREATE_TIMESTAMP)
.on("value", (snapshot) => {
const messages = [];
snapshot.forEach((snap) => {
const obj = snap.val();
obj[MESSAGE_ID] = snap.key;
messages.push(obj);
});
const new_state = {};
new_state[MESSENGER_MESSAGES] = messages;
console.log("New state");
console.log(new_state);
this.setState(new_state);
});
}
this.setState(change_object);
//some event listeners that should persist until destroyed
}
async componentDidUpdate() {
//Allows reloading
console.log("ComponentDidUpdate occurred");
const change_object = {};
let boolean_one = false;
let boolean_two = false;
let boolean_three = false;
if (this.state[CHANNEL_ID]) {
const channel_data = await f2();
const members_value = channel_data["ids"];
const channel_title_value = channel_data["name"];
change_object["ids"] = members_value;
change_object["name"] = channel_title_value;
//Listener for upcoming messages
await realtime_db.ref(`${MESSAGES_REALTIME_REFERENCE}/${this.state[MESSENGER_ACTIVE_CHANNEL_ID]}`)
.orderByChild(MESSAGE_CREATE_TIMESTAMP)
.on("value", (snapshot) => {
//Currently handling all setState logic inside the callback function to output correctly
const messages = [];
snapshot.forEach((snap) => {
const obj = snap.val();
obj[MESSAGE_ID] = snap.key;
messages.push(obj);
});
//some code for deciding whether to set state
const deciding_boolean = boolean_three || boolean_one && boolean_two;
if (deciding_boolean) {
this.setState(change_object);
}
});
}
}
From some console.logs, whenever I did a handleSend
that causes setState
, re-rendering and subsequently causes componentDidUpdate
to setState
again, re-render and go to componentDidUpdate
for the 2nd time and rejecting new updates until this cycle occurs again.
Even though it shows correctly for now, componentDidUpdate
was hit twice, and I always end up fetching my database queries and callbacks twice, which might give me additional running costs that I’m not aware of. I want to know if there’s generally a better coding practice to reduce this duplication of code as well as cost factors.