1
votes

I've been stuck on this error for a while now, I've not been able to get it resolved searching Stack and Google. I have a ProfileScreen.js to display a user's profile. But when you click to view the profile I get this error: Cast to ObjectId failed for value "undefined" at path "_id" for model "User". From the searching I've done, I've tried rolling my version of Mongoose back, but that didn't help. Anyone have any ideas?

userRouter.js

import express from 'express';
import expressAsyncHandler from 'express-async-handler';
import bcrypt from 'bcryptjs';
import data from '../data.js';
import User from '../models/userModel.js';
import { generateToken } from '../utils.js';

const userRouter = express.Router();

userRouter.get('/seed', expressAsyncHandler(async (req, res) => {
  // await User.remove({});
  const createdUsers = await User.insertMany(data.users);
  res.send({ createdUsers });
}));

userRouter.post('/signin', expressAsyncHandler(async (req, res) => {
  const user = await User.findOne({ email: req.body.email });
  if(user) {
    if(bcrypt.compareSync(req.body.password, user.password)) {
      res.send({
        id: user._id,
        name: user.name,
        email: user.email,
        isAdmin: user.isAdmin,
        token: generateToken(user),
      });
      return;
    }
  }
  res.status(401).send({ message: 'Invalid email or password' });
}));

userRouter.post('/register', expressAsyncHandler(async(req, res) => {
  const user = new User({name: req.body.name, email: req.body.email,
    password: bcrypt.hashSync(req.body.password, 8),
  });
  const createdUser = await user.save();
  res.send({
    id: createdUser._id,
    name: createdUser.name,
    email: createdUser.email,
    isAdmin: createdUser.isAdmin,
    token: generateToken(createdUser),
  })
})
);

userRouter.get(
  '/:id',
  expressAsyncHandler(async (req, res) => {
    const user = await User.findById(req.params.id);
    if (user) {
      res.send(user);
    } else {
      res.status(404).send({ message: 'User Not Found' });
    }
  })
);

export default userRouter;

ProfileScreen.js

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { detailsUser } from '../actions/userActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';

export default function ProfileScreen() {
  const userSignin = useSelector((state) => state.userSignin);
  const { userInfo } = userSignin;
  const userDetails = useSelector((state) => state.userDetails);
  const { loading, error, user } = userDetails;
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(detailsUser(userInfo._id));
  }, [dispatch, userInfo._id]);
  const submitHandler = (e) => {
    e.preventDefault();
    // dispatch update profile
  };
  
  return (
    <div>
      <form className="form" onSubmit={submitHandler}>
        <div>
          <h1>User Profile</h1>
        </div>
        {loading ? (
          <LoadingBox></LoadingBox>
        ) : error ? (
          <MessageBox variant="danger">{error}</MessageBox>
        ) : (
          <>
            <div>
              <label htmlFor="name">Name</label>
              <input 
                id="name"
                type="text"
                placeholder="Enter name"
                value={user.name}>
                </input>
            </div>
            <div>
              <label htmlFor="email">Email</label>
              <input 
                id="email"
                type="email"
                placeholder="Enter email"
                value={user.email}
               >
                </input>
            </div>
            <div>
              <label htmlFor="password">Password</label>
              <input 
                id="password"
                type="password"
                placeholder="Enter password">
                </input>
            </div>
            <div>
              <label htmlFor="confirmPassword">Confirm Password</label>
              <input 
                id="confirmPassword"
                type="password"
                placeholder="Confirm password"
                >
                </input>
            </div>
            <div>
              <label/>
              <button className="primary" type="submit">Update Profile</button>
            </div>
          </>
          )}
      </form>
    </div>
  )
}

I screwed up and posted the wrong code, I posted the user router code instead of the ProfileScreen code. I've added the ProfileScreen code.

Thanks in advance for ANY help.

-N8

2
I am assuming this is the line that throws the error const user = await User.findById(req.params.id);. Have you confirmed that you're actually requesting the correct URL by looking at the Chrome network tab? Or wherever you make your network request from? Your client side code is a prime suspect here.codemonkey
I edited my question to actually include the code for the ProfileScreen, I'd posted the router code by mistake.Nathan Huber
In your useEffect, can you put this console.log("User info", userInfo) right above dispatch(detailsUser(userInfo._id));? And then check the console to see what gets printed?codemonkey
It returns the user object:Nathan Huber
User info {id: "60408c4b912e51879c7c08c4", name: "Matt Sanders", email: "[email protected]", isAdmin: false, token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2M…TU1fQ.PmUoyuJqj2cvnGVYkj21NcDP6BR3S1Ea7NOBg5-Arjs"}Nathan Huber

2 Answers

1
votes

The issue seems to be right here:

dispatch(detailsUser(userInfo._id));

Which should, according to your user object, look like:

dispatch(detailsUser(userInfo.id));

Since _id is undefined as it does not exist in the object, you end up posting to /undefined instead of /60408c4b912e51879c7c08c4. Hence the error.

0
votes

this error comes when mongoose is not able to cast the req.params.id on this line: const user = await User.findById(req.params.id); to an ObjectId

undefined is not castable, and anyway you won't find any doc with undefined as an _id either. You might want to check if req.params.id is undefined, and then return something based on that.

See here to see what a castable objectId is!