0

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);
    });
}
Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
  • Instead of async-await could you try returning a promise from your API call and handle that? – Satyaki Jan 17 '19 at 06:54
  • did you see the result of console.log(projects) in fetchProject function? – sdkcy Jan 17 '19 at 07:09
  • Satyaki, thank you for taking time to make your suggestion! In effect, the solution below by tbuglc elaborates on that same idea. I really appreciate your taking the time to contribute to the solution! – Joel Morgan Jan 17 '19 at 22:15
  • sdkcy, thank you for asking that question! I was getting "undefined" in that console.log in fetchProjects(), but I was seeing data in the console.log in getProjects(), which helped me identify that the breakdown was happening between those two points. – Joel Morgan Jan 17 '19 at 22:17

1 Answers1

1

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.

tbuglc
  • 180
  • 9
  • Check this [codesandbox](https://codesandbox.io/s/8zz2x7yo59) I created to simulate your issue – tbuglc Jan 17 '19 at 08:14
  • Ch-ching!! That did it! Just simply adding that "return" at the top solved the problem. Thank you so much, tbuglc! And I'll definitely take your redux suggestion into consideration. For now, I'm glad to know that I have a significantly less complex way forward. Thanks again for taking the time to help! – Joel Morgan Jan 17 '19 at 22:12
  • @JoelMorgan Happy to hear that :-) – tbuglc Jan 18 '19 at 08:36