Async/await in componentDidMount to load in correct order

Viewed 6470

I am having some troubles getting several functions loading in the correct order. From my code below, the first and second functions are to get the companyID companyReference and are not reliant on one and another.

The third function requires the state set by the first and second functions in order to perform the objective of getting the companyName.

async componentDidMount() {
    const a = await this.companyIdParams();
    const b = await this.getCompanyReference();
    const c = await this.getCompanyName();
    a;
    b;
    c;
  }

  componentWillUnmount() {
    this.isCancelled = true;
  }

  companyIdParams = () => {
    const urlString = location.href;
    const company = urlString
      .split('/')
      .filter(Boolean)
      .pop();
    !this.isCancelled &&
      this.setState({
        companyID: company
      });
  };

  getCompanyReference = () => {
    const { firebase, authUser } = this.props;
    const uid = authUser.uid;
    const getUser = firebase.user(uid);
    getUser.onSnapshot(doc => {
      !this.isCancelled &&
        this.setState({
          companyReference: doc.data().companyReference
        });
    });
  };

  getCompanyName = () => {
    const { firebase } = this.props;
    const { companyID, companyReference } = this.state;
    const cid = companyID;
    if (companyReference.includes(cid)) {
      const getCompany = firebase.company(cid);
      getCompany.onSnapshot(doc => {
        !this.isCancelled &&
          this.setState({
            companyName: doc.data().companyName,
            loading: false
          });
      });
    } else if (cid !== null && !companyReference.includes(cid)) {
      navigate(ROUTES.INDEX);
    }
  };

How can I achieve this inside componentDidMount?

2 Answers

setState is asynchronous, so you can't determinate when the state is updated in a sync way.

1) I recommend you don't use componentDidMount with async, because this method belongs to react lifecycle.

Instead you could do:

componentDidMount() {
  this.fetchData();
}

fetchData = async () => {
  const a = await this.companyIdParams();
  const b = await this.getCompanyReference();
  const c = await this.getCompanyName();
}

2)

The companyIdParams method doesn't have a return, so you are waiting for nothing. If you need to wait I would return a promise when setState is finished;

companyIdParams = () => {
  return new Promise(resolve => {
    const urlString = location.href;
    const company = urlString
      .split('/')
      .filter(Boolean)
      .pop();
    !this.isCancelled &&
      this.setState({
        companyID: company
      }, () => { resolve() });
  });
};

The same for getCompanyReference:

getCompanyReference = () => {
  return new Promise(resolve => {
    const { firebase, authUser } = this.props;
    const uid = authUser.uid;
    const getUser = firebase.user(uid);
    getUser.onSnapshot(doc => {
      !this.isCancelled &&
        this.setState({
          companyReference: doc.data().companyReference
        }, () => { resolve() });
    });
  });
};

3)

If you want to parallelize the promises, you could change the previous code to this:

const [a, b] = await Promise.all([
  await this.companyIdParams(),
  await this.getCompanyReference()
]);

4)

According to your code, the third promise is not a promise, so you could update (again ;) the above code:

const [a, b] = .....
const c = this.getCompanyName()

EDIT: the bullet points aren't steps to follow

As the last api call is dependent on the response from the first 2 api calls, use a combination of Promise.all which when resolved will have the data to make the last dependent call

async componentDidMount() {
    let [a, c] = await Promise.all([
        this.companyIdParams(),
        this.getCompanyReference()
    ]);

    const c = await this.getCompanyName();
}

Related