1

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.

Prashin Jeevaganth
  • 1,223
  • 1
  • 18
  • 42

1 Answers1

0

if you check at react docs you will see that componentDidUpdate has as parameters prevProps, prevState and snapshot.

with that information the most common approach to avoid infinite loops and recalls on componentDidUpdate is to compare if prevState is different from this.state (or some specific piece of state you are monitoring). you also can compare props as well, though it's not your case here.

you can write like:

componentDidUpdate(prevProps, prevState) {
  \\ you can specify which part of state you want to check
  if (prevState === this.state) return
  \\ code to run
}

or wrapping the code inside the if statement:

componentDidUpdate(prevProps, prevState) {
  if (prevState !== this.state) {
    \\ code to run
  }
}
buzatto
  • 9,704
  • 5
  • 24
  • 33