Search code examples
databasemongodbserverreact-routernosql

Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "card"


I have a site with cards and when you click on some card you need to see the same card but with another details. But everytime I click on some card I get this massgae in the terminal: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "card"

Here What I wrote:

** CardDatails.jsx**

import React, { Component } from "react";
import PageHeader from "../common/pageHeader";
import { getCard } from "../../services/cardService";

class CardDetails extends Component {
  state = {
    card: {
      adress: "",
      bizNumber: "",
      createdAt: "",
      description: "",
      image: { url: "", alt: "" },
      likes: [],
      phone: "",
      title: "",
    },
    errors: {},
    isMounted: false,
  };

  async componentDidMount() {
    try {
      const { data } = await getCard(this.props.id);
      this.setState({ card: { card: data, isMounted: true } });
    } catch (error) {
      this.setState({ errors: error.massage });
    }
  }

  render() {
    const { card } = this.state;
    const {
      title,
      description,
      image: { url, alt },
      address,
      phone,
      bizNumber,
      createdAt,
      likes,
    } = card;

    return (
      <React.Fragment>
        <PageHeader
          title={`Business Card of: ${title}`}
          subTitle={`Businness Description: ${description}`}
        />
        <div className="card-body p-2">
          <div>
            <img
              style={{ maxWidth: "400px" }}
              src={url}
              className="card-img"
              alt={alt}
            ></img>
            <br />
            <strong>Tel: </strong>
            {phone}
          </div>
          <div>
            <strong>Address: </strong>
            {address}
          </div>
          <div>
            <strong>Card Number: </strong>
            {bizNumber}
          </div>
          <div>
            <strong>Created At: </strong>
            {createdAt}
          </div>
          <div>
            <strong>likes: </strong>
            {likes}
          </div>
        </div>
      </React.Fragment>
    );
  }
}

export default CardDetails;

App.js:

import { useParams } from "react-router-dom";
import CardDetails from "./CardDetails";

const CardDetailsConvertor = () => {
  const { id } = useParams();
  return <CardDetails id={id} />;
};

export default CardDetailsConvertor;


import HomePage from "./layout/main/Home";
import About from "./layout/main/About.jsx";
import { Routes, Route } from "react-router-dom";
import Error404 from "./layout/main/Error404";
import Header from "./layout/header/Header.jsx";
import Footer from "./layout/footer/footer.jsx";
import BizSignup from "./layout/main/BizSignup";
import Logout from "./layout/main/Logout";
import MyCards from "./layout/main/MyCards";
import MyFavoriteCards from "./layout/main/MyFavoriteCards";
import Login from "./layout/main/Login";
import Signup from "./layout/main/Signup";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { getCurrentUser } from "./services/userService";
import CreateCard from "./layout/main/CreateCard";
import EditCardConvertor from "./layout/main/editCardConvertor";
import CardDetails from "./layout/main/CardDetails";
import CardDetailsConvertor from "./layout/main/CardDetailsConvertor";

function App() {
  const user = getCurrentUser();
  return (
    <div className="App">
      <Header user={user} />
      <ToastContainer />

      <main style={{ minHeight: "85vh" }}>
        <Routes>
          <Route path="/about" element={<About />} />
          <Route path="/biz-signup" element={<BizSignup />} />
          <Route path="/logout" element={<Logout />} />
          <Route path="/my-cards" element={<MyCards user={user} />} />
          <Route path="/create-card" element={<CreateCard user={user} />} />
          <Route
            path="/card-details-converotr"
            element={<CardDetailsConvertor user={user} />}
          />
          <Route
            path="/edit-card/:id"
            element={<EditCardConvertor user={user} />}
          />
          <Route path="/card-details/:id" element={<CardDetails />} />
          <Route
            path="/my-fav-cards"
            element={<MyFavoriteCards user={user} />}
          />
          <Route path="/login" element={<Login />} />
          <Route path="/signup" element={<Signup />} />
          <Route path="/" element={<HomePage />} />
          <Route path="*" element={<Error404 />} />
        </Routes>
      </main>

      <Footer />
    </div>
  );
}

export default App;




**CardDetailsConverotr.jsx:**
import { useParams } from "react-router-dom";
import CardDetails from "./CardDetails";

const CardDetailsConvertor = () => {
  const { id } = useParams();
  return <CardDetails id={id} />;
};

export default CardDetailsConvertor;

So if you can help I will be happy Thank you!


Solution

  • CardDetails component isn't passed any props, so this.props.id is undefined.

    <Route
      path="/card-details/:id"
      element={<CardDetails />} // <-- no props are passed here
    />
    

    You can either convert CardDetails to a functional component so it can use the useParams hook to access the id route param, or you can create a custom withRouter Higher Order Component to do this and inject the params as a prop to be accessed in component.

    I won't cover conversion, but the following is a simple withRouter HOC:

    import { useParams } from 'react-router-dom';
    
    const withRouter = Component => props => {
      const params = useParams();
      return <Component {...props} params={params} />;
    };
    

    ...

    import React, { Component } from "react";
    import PageHeader from "../common/pageHeader";
    import { getCard } from "../../services/cardService";
    import { withRouter } from "../path/to/withRouter";
    
    class CardDetails extends Component {
      state = {
        ...
      };
    
      async componentDidMount() {
        try {
          const { data } = await getCard(this.props.params.id);
          this.setState({ card: { card: data, isMounted: true } });
        } catch (error) {
          this.setState({ errors: error.massage });
        }
      }
    
      render() {
        const { card } = this.state;
        const {
          title,
          description,
          image: { url, alt },
          address,
          phone,
          bizNumber,
          createdAt,
          likes,
        } = card;
    
        return (
          ...
        );
      }
    }
    
    export default withRouter(CardDetails);