Rendering MongoDB entries by ID inside React form inputs

I have built an ‘edit’ page which is attempting to pull an individual object in my MongoDB Database, by using ‘findById’ in a serverless function hosted by Vercel.

The response data I’m then attempting to use to populate the ‘value’ of different inputs on this ‘edit’ page.

However, I’m getting no response at all from my call (not even an error message, neither in my console or my function logs on Vercel) - the page is loading successfully, but no data is being put into the inputs, they’re coming through as blank.

It’s worth noting - the URL is the MongoDB ID for the object that’s being called!

I have tried refactoring my code, searching for solutions, going through tutorials, but nothing seems to be working. It’s frustrating as I’m just not getting any feedback from my app, so don’t know what I’m doing wrong?

If anyone has any feedback on my code, that’d be appreciated. Please let me know if I’ve missed anything out below.

The ‘child’ and ‘parent’ components are just how the page gets navigated to.

My ‘route’ via React Router:

<Route path="/:id" component={EditDebt} />

My MongoDB schema:

const SubmitDebtSchema = new Schema ({
  creditCard: String,
  personalLoan: String,
  provider: String,
  balance: Number,
  limit: Number,
  monthly: Number,
  interest: Number,
  borrowed: Number
});

module.exports = mongoose.model('submitdebts', SubmitDebtSchema);

My ‘child’ component which uses props to fill the link using the response MongoDB ID.

<div className="debt-card-edit-container"><a className="debt-card-edit-link" href={`${props.edit}`}>Edit</a></div>

My ‘parent’ component which fills the prop in using a database call:

<IndividualDebtCard edit={debt._id} />

My ‘fetch’ serverless function:

const mongoose = require("mongoose");
const SubmitDebt = require("./submitDebtSchema");

require("dotenv").config();

mongoose.connect(process.env.MONGO_URI);

  module.exports = async (req, res) => {
    let id = req.params.id;
    await SubmitDebt.findById(id, function(err, submitdebts) {
      return res.status(200).json(submitdebts);
    });
};

Finally, and most importantly, my actual ‘edit’ component in full…

class EditDebt extends Component {
  constructor(props) {
    super(props)

    this.state = {
      provider: '',
      balance: '',
      limit: '',
      monthly: '',
      interest: '',
      borrowed: ''
    }

    this.toggleCreditCard = this.toggleCreditCard.bind(this);
    this.togglePersonalLoan = this.togglePersonalLoan.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    axios.get("/api/fetchEditDebt"+this.props.match.params.id)
      .then((res) => {

        this.setState({
          provider: res.data.provider,
          balance: res.data.balance,
          limit: res.data.limit,
          monthly: res.data.monthly,
          interest: res.data.interest,
          borrowed: res.data.borrowed
        })
        console.log(res)
        console.log(this.state.provider)
      })
      .catch(e => {
        console.log(e)
      })
      }

  onChange = (e) => {
        this.setState({ [e.target.name]: e.target.value})
    }

  render() {

    return (
      <section className="edit-debt-section">
        <div className="edit-debt-container">
          <DashboardReturn />

          <form className="edit-debt-form" method="POST">
            <div className="edit-debt-form-content">
              <h2 className="edit-debt-heading">Edit your linked debt</h2>

                    <label className="edit-debt-label">Balance</label>
                    <div className="edit-debt-input-container">
                      <h6 className="edit-debt-input-text">£</h6>
                      <input className="edit-debt-number-input" type="number" name="balance" value={this.state.balance} onChange={this.onChange} step="0.01" />
                    </div>

                        <label className="edit-debt-label">Amount borrowed</label>
                        <div className="edit-debt-input-container">
                          <h6 className="edit-debt-input-text">£</h6>
                          <input className="edit-debt-number-input" type="number" name="borrowed" value={this.state.borrowed} onChange={this.onChange} step="0.01" />
                        </div>

                        <label className="edit-debt-label">Credit limit</label>
                        <div className="edit-debt-input-container">
                          <h6 className="edit-debt-input-text">£</h6>
                          <input className="edit-debt-number-input" type="number" name="limit" value={this.state.limit} onChange={this.onChange} step="0.01" />
                        </div>

                        <label className="edit-debt-label">Monthly repayment</label>
                        <div className="edit-debt-input-container">
                          <h6 className="edit-debt-input-text">£</h6>
                          <input className="edit-debt-number-input" type="number" name="monthly" value={this.state.monthly} onChange={this.onChange} step="0.01" />
                        </div>

                        <label className="edit-debt-label">Interest rate?</label>
                        <div className="edit-debt-input-container">
                          <input className="edit-debt-number-input-alt" type="number" name="interest" value={this.state.interest} onChange={this.onChange} step="0.01" />
                          <h6 className="edit-debt-input-text-alt">%</h6>
                        </div>

           </form>
        </div>
      </section>
    )
  }
}

Sorry about all of the code, and please let me know if I’ve missed anything which is really important in my question.

Thank you

EDIT: I’ve added a couple of console logs in to my Axios function. The ‘this.state.provider’ is undefined, and this is the object res that gets returned:

Object

config: {url: "/api/fetchEditDebt/5f8da21ba227e300076c3299", method: "get", headers: {Accept: "application/json, text/plain, */*"}, transformRequest: Array, transformResponse: Array, …}

data: "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/><lin…"

headers: {access-control-allow-origin: "*", age: "41", cache-control: "s-maxage=0", content-disposition: "inline; filename=\"index.html\"", content-encoding: "br", …}

request: XMLHttpRequest {listeners: Object, onreadystatechange: function, readyState: 4, timeout: 0, withCredentials: false, …}

status: 200

statusText: ""

Don’t know if this helps?

Hello there,

It does not look as though you are sharing the code related to the error.

Some things I suspect:

  1. When using the MongoDB driver to search by id, the id must specifically be of type ObjectId
  2. You are not allowing the DB call to finish, before running whatever code is sending:
res.json({reponseFromDB: response.id})
  1. MongoDB will send a document object with an _id property (by default), not a id property.

Hope this helps

1 Like

Hey,

I’m sorry - I don’t understand this. I’m using Mongoose too, so that will change how I access the ID too?

The error is originating from my ‘fetch’ serverless function I believe. So this code:

const mongoose = require("mongoose");
const SubmitDebt = require("./submitDebtSchema");

require("dotenv").config();

mongoose.connect(process.env.MONGO_URI;

  module.exports = async (req, res) => {
  const submitDebt = await SubmitDebt.findById(req.params.id);
  return res.status(200).json(submitDebt);
};

Where am I not allowing the call to finish too? This works in other ‘fetch’ functions I have… I think the problem is relating to ‘req.params.id’, but can’t figure it out.

Any additional feedback would be appreciated!

Right. Some things I suggest:

  1. This contains a syntax error:
mongoose.connect(process.env.MONGO_URI;
  1. Debug, by console.logging:
req.params

Also, where are you using that exported function? I do not see a route where you can get id anywhere.

Hey

So the mongoose.connect is fine, I basically edited it to reduce the text on there. That’s my bad - it works in my app.

This is being done through serverless functions. That means the exported function (i.e the ‘fetch’) is being called in my axios.get - the path is shown in the axios call, which is within componentDidMount. So I don’t see a reason why ID wouldn’t be available?

Does that make sense?

I might be missing something, but where are you defining the routes?

I would expect something like this to be on the server side:

app.get('/api/fetchEditDebt/:id', async (req, res) => {
  const submitDebt = await SubmitDebt.findById(req.params.id);
  return res.status(200).json(submitDebt);
});

Then a param named id is made available. And you call it with:

axios.get("/api/fetchEditDebt/123456789")

To be honest, my knowledge of serverless is extremely sketchy. So, if I am being naive, then please forgive me, and let me know.

Otherwise, is there any chance of you being able to share a CodeSandbox/Repl.it/Glitch of your project?

I’m hosting it with Vercel, where you can use serverless functions by storing them inside an ‘api’ folder. This then gets called in your front-end React code. Serverless functions with Vercel are essentially just back-end code which get called every time you make a request. They’re like individual functions without the back-end infrastructure.

Since I’m using React though, I am using React Router. This is my React Router route in my App.js if this helps?

<Route path="/:id" component={EditDebt} />

My path is then defined in this href (a child component) via a prop:

<a className="debt-card-edit-link" href={${props.edit}}>Edit</a>

And then executed inside the parent component like this:

<IndividualDebtCard edit={debt._id} />

Sorry about this, really appreciate your help with this… I’m probably being pretty annoying, so appreciate you coming back to clarify.

I think my issue is there isn’t much out there for serverless and Mongoose ‘findById’ - documentation is pretty sketchy too for it in my opinion.

Any other thoughts?

This helps a tonne. Thank you.

I would assume the issue is here, but it is difficult to tell, without more code. Reason being, what is /:id relative to?

I really do encourage you to console.log as much as you can around the req to see if anything is getting through.

Hi. I have made some changes to my code, so I will update my original question…

I’m now no longer getting the ID undefined error. In fact, I’m getting no error at all…

I’m not sure if the updated code will help, it feels ‘closer’…

And sorry, in response to console.log - I was getting a 502 error before I made the above changes. The error I put about ‘id’ being undefined was in my function logs on Vercel.

I don’t understand about ‘what is /:id relative to’ as well - if you could explain that it’d be appreciated.

Thanks for the update.

This might be silly, but what happens if you replace:

<a className="debt-card-edit-link" href={`${props.edit}`}>Edit</a>

With a Link component where props.edit is the value of the to property?

Well, you are wanting id to be equal to whatever edit._id is, and are redirecting with href={id}. So, one thing I wonder is what if you did this (or similar with my above comment):

<a href={`/${props.edit}`}>Edit</a>

What happens, if you hardcode props.edit into the href?
1)

<a href='123456789'></a>
<a href='/12345678'></a>

Hey - I’ve tried everything you’ve suggested, and it’s all just the same result. The page ‘loads’, but there’s no information pulling through from my database.

I really appreciate the help. I might just have to go back to basics and restart the code.