I’m new to react and I need some help when I´m trying to bring some “products” from an array of objects that I have in one file of my react app called “products.js”.
The thing is that I am able to draw the products within the array in the Home Screen. However when I try to draw those products on my details products page (this one is a page where I can see an individual product details) “ProductDetails.js” I’m getting the error (regardless if I try to bring product.name or product.price or whatever):
TypeError: Cannot read properties of undefined (reading ‘name’)
In order for you to have an idea about what I’m talking about I will share to you some snippets from my code to put you in context. I will Start with the exact error message and I will finish with a Glance of My Project Dependencies.
Error Message
TypeError: Cannot read properties of undefined (reading 'name') ProductDetails C:/Users/jjimennz/OneDrive/Desktop/eCommerce MERN Stack/Sprint1/frontend/src/views/ProductDetails.js:19 16 | const ProductDetails = ({ match }) => { 17 | const product = products.find((p) => p._id === match.params.id); 18 | > 19 | return <div>{product.name}</div>; 20 | }; 21 | 22 | export default ProductDetails;
this is the View I’m having TROUBLE with and is not only with the “product.name” I happens with anything “product.price” “product.image”… this is the details product screen, the view that is supposed to show me each individual product with its details on its own page, which I called “ProductDetails.js”
import React from "react"; import products from "../products"; const ProductDetails = ({ match }) => { const product = products.find((p) => p._id === match.params.id); return <div>{product.name}</div>; }; export default ProductDetails;
This is the File with the array containing the products I want to draw on the above product details screen. this file is the one called “products.js”:
const products = [ { _id: 1, name: "Nemeziz 19.4", image: "https://assets.adidas.com/images/w_600,f_auto,q_auto/75987ad2e31644d59d24ab3000fce766_9366/Botines_Nemeziz_19.4_Pasto_Sintetico_Verde_FV3317_41_detail.jpg", description: "Breve descripcion de los zapatos", brand: "Adidas", category: "Zapatos", price: 89.99, countInStock: 20, rating: 4.5, numReviews: 14, }, { _id: 2, name: "Adidas Ace", image: "https://www.sports-ws.com/img/item/F163AD0/F163AD0654.jpg", description: "Breve descripcion de los zapatos", brand: "Adidas", category: "Zapatos", price: 149.99, countInStock: 8, rating: 4.5, numReviews: 15, }, ]; export default products
This is the view for the Home Screen, where I show all of the products within the above array, this one works just fine and draws every product within the array on the screen. this one is called “HomeScreen.js”
import React from "react"; import { Row, Col } from "react-bootstrap"; import Product from "../components/Product"; import products from "../products"; const HomeScreen = () => { return ( <> <h1>Latest Products</h1> <Row> {products.map((product) => ( <Col key={product._id} sm={12} md={6} lg={4} xl={3}> <Product product={product} /> </Col> ))} </Row> </> ); }; export default HomeScreen;
This is the Product component, this one basically draws each individual product within the array on the above Home Screen view. This one as the above Home Screen also works just fine, it brings me the product with its name, its price and image. I did call this component “Product.js”*
import React from "react"; import { Link } from "react-router-dom"; import { Card } from "react-bootstrap"; import Rating from "./Rating"; const Product = ({ product }) => { return ( <Card className="my-3 p-3 rounded"> <Link to={`/product/${product._id}`}> <Card.Img src={product.image} variant="top" /> </Link> <Card.Body> <Link to={`/product/${product._id}`}> <Card.Title as="div"> <strong>{product.name}</strong> </Card.Title> </Link> <Card.Text as="div"> <Rating value={product.rating} text={`${product.numReviews} reviews`} /> </Card.Text> <Card.Text as="h3">${product.price}</Card.Text> </Card.Body> </Card> ); }; export default Product;
“App.js” file, so you have a better understanding about what is going on on my project
import React from "react"; import { BrowserRouter as Router, Route } from "react-router-dom"; import { Container } from "react-bootstrap"; import Header from "./components/Header"; import Footer from "./components/Footer"; import HomeScreen from "./views/HomeScreen"; import ProductDetails from "./views/ProductDetails"; function App() { return ( <Router> <Header /> <main className="py-3"> <Container> <Route path="/" component={HomeScreen} exact /> <Route path="/product/:id" component={ProductDetails} /> </Container> </main> <Footer /> </Router> ); } export default App;
This is My “package.json” file with dependencies in my project, just in case It helps to help me with this
{ "name": "frontend", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", "react": "^17.0.2", "react-bootstrap": "^2.0.0", "react-dom": "^17.0.2", "react-router-bootstrap": "^0.25.0", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", "web-vitals": "^1.1.2" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
Here you have an Screenshot of the error message on the products details view
Here you have an Screenshot of the page that works the Home Screen
Advertisement
Answer
Your issue is that your .find()
method is returning undefined
, so you can’t access properties on product
such as .name
as it is undefined
. The .find()
method will return undefined
when the callback function doesn’t return a truthy value for any of your items within your array.
In this case, your callback will only return true
when your product id matches the id exactly (in both value and type) of the id used in your URL:
p._id === match.params.id
However, the above will never be true
, as p._id
in your data is a number type, and match.params.id
is a string type, so the two are never considered equal under strict equality ===
. As a result, .find()
never finds a match and you get undefind
. Instead, you can use “loose” equality comparison ==
, which doesn’t need the types to match:
const product = products.find((p) => p._id == match.params.id);
Alternatively, you can convert the type manually using a method such as Number()
and stick with ===
:
const product = products.find((p) => p._id === Number(match.params.id));
Other things to try
I’ve noticed that this question has gathered quite a lot of attention, so I thought I’d add another common reason why you might see this error (note these reasons don’t apply to OPs code).
Firstly, the error means that whatever you’re using .name
on has a value of undefined
. Typically this can happen if you’re making an API call with something like fetch()
or doing some other asynchronous task within a useEffect()
which sets the value of whatever you’re using .name
on. There are two ways to combat this issue, one way is to use optional chaining to prevent access of the .name
property if the object is null
or undefined
, eg:
<strong>{product?.name}</strong>
Or, another way is to assign your state value so that it initially isn’t undefined, eg:
const [product, setProduct] = useState({}); // <-- using `product.name` now works, whereas doing `useState()` wouldn't work