Search code examples
reactjsreact-async

React – Async / Await doesn’t seem to be waiting


I had previously posted this problem under the error message of “Cannot read property 'data' of undefined”. But in the process of digging into it for several hours, I’ve discovered that my problem really boils down to the fact that my “async / await” doesn’t seem to be . . . . awaiting! And yes, I did check the several other versions of this question that have already been asked here. :)

I’m building a React form that will have several drop-down boxes populated with data from MongoDB. I’m relatively new to React and a beginner with MongoDB.

I’ve been able to successfully get the data into the drop-down by just cramming all of the code into one file. Now, I’m trying to start refactoring the code by properly splitting pieces into separate files. And that’s where I’ve run into this “data delay” issue.

When “componentDidMount” calls the “fetchProjects” function, that function goes and grabs a list of projects from MongoDB using the “getProjects” function in a “projects service” file. When the console.log within “fetchProjects” runs, it’s coming back as undefined. But then after the data set comes back as undefined (and errors out the process), I do get a console log of the projects object array from the “getProjects” function.

I’ve been able to make this process work with hard-coded object array data in the “getProjects” function, so that tells me that the problem lies in the amount of time required to actually get the data back from MongoDB.

Please tell me there’s a way to solve this without using Redux! :D

Here’s my App.js file –

import React, { Component } from "react";
import "./App.css";
import { getProjects } from "./services/svcProjects";

class App extends Component {
  state = {
    data: {
      selProject: ""
    },
    projects: []
  };

  async componentDidMount() {
    await this.fetchProjects();
  }

  async fetchProjects() {
    const { data: projects } = await getProjects();
    console.log(projects);
    this.setState({ projects });
  }

  render() {
    return (
      <div className="App">
        <h1>Project Log</h1>
        <label htmlFor={"selProject"}>{"Project"}</label>
        <select name={"selProject"} id={"selProject"} className="form-control">
          <option value="" />
          {this.state.projects.map(a => (
            <option key={a._id} value={a.project}>
              {a.project}
            </option>
          ))}
        </select>
      </div>
    );
  }
}

export default App;

And here’s the “projects service” file. Again, please note that the console.log statements here show that I’m still getting data back from MongoDB. That data is just taking too long to arrive back in the App.js file.

Also, by the way, I realize that having my Mongo connection info in this file is a huge security hole. I’ll be fixing that later.

import {
  Stitch,
  RemoteMongoClient,
  AnonymousCredential
} from "mongodb-stitch-browser-sdk";

export function getProjects() {
  const client = Stitch.initializeDefaultAppClient("------");
  const db = client
    .getServiceClient(RemoteMongoClient.factory, "-----")
    .db("----------");
  client.auth
    .loginWithCredential(new AnonymousCredential())
    .then(() =>
      db
        .collection("--------")
        .find({}, { sort: { Project: 1 } })
        .asArray()
    )
    .then(res => {
      console.log("Found docs", res);
      console.log("[MongoDB Stitch] Connected to Stitch");
      return res;
    })
    .catch(err => {
      console.error(err);
    });
}

Solution

  • I think adding a return into your getProjects() service will solve your issue.

    import {
      Stitch,
      RemoteMongoClient,
      AnonymousCredential
    } from "mongodb-stitch-browser-sdk";
    
    export function getProjects() { //add async if you need to await in func body
      const client = Stitch.initializeDefaultAppClient("------");
      const db = client
        .getServiceClient(RemoteMongoClient.factory, "-----")
        .db("----------"); // if these above are async, then await them as well.
    
        // added return keyword here
        return client.auth // should return Promise to await in your component
        .loginWithCredential(new AnonymousCredential())
        .then(() =>
          db
            .collection("--------")
            .find({}, { sort: { Project: 1 } })
            .asArray()
        )
        .then(res => {
          console.log("Found docs", res);
          console.log("[MongoDB Stitch] Connected to Stitch");
          return res;
        })
        .catch(err => {
          console.error(err);
        });
    }
    

    Edit 1:

    For refactoring, I think pairing redux and redux-saga will give you very good separation of concern and a way to easily write test if you plan to do so. But overall, I think this tweak above can at least solve your issue.