React页面刷新报错:TypeError: 无法读取undefined的categoryName属性
问题:React组件刷新时访问嵌套属性报错TypeError
我写了一个ProductDetail组件,通过Axios封装的ProductService获取产品详情。但只要用到product.category.categoryName,页面刷新就会抛出TypeError: Cannot read properties of undefined (reading 'categoryName')错误。
ProductDetail组件代码
import React, { useState, useEffect } from 'react' import { useParams } from 'react-router' import { Button, Card, Image } from 'semantic-ui-react' import ProductService from '../services/productService' export default function ProductDetail() { const { id } = useParams() const [product, setProduct] = useState({}); useEffect(() => { let productService = new ProductService(); productService.getProductById(id).then(result => setProduct(result.data.data)) }, []) return ( <div> <Card.Group> <Card fluid> <Card.Content> <Image size='medium' src='https://react.semantic-ui.com/images/avatar/large/steve.jpg' /> <Card.Header style={{ marginTop: '0.5em' }}>{product.productName}</Card.Header> <Card.Meta></Card.Meta> <Card.Description>Units in Stock : {product.unitsInStock} </Card.Description> <Card.Description>Unit Price : {product.unitPrice}$ </Card.Description> <Card.Description> {product.category.categoryName} </Card.Description> </Card.Content> <Card.Content extra> <div className='ui two buttons'> <Button basic color='green'> Add to Cart </Button> <Button basic color='red'> Add to Favorites </Button> </div> </Card.Content> </Card> </Card.Group> </div> ) }
ProductService类代码
import axios from "axios"; export default class ProductService{ getProducts(){ return axios.get("http://localhost:8080/api/products/getAll") } getProductById(productId){ return axios.get(`http://localhost:8080/api/products/getByProductId?productId=${productId}`) } }
错误信息
API返回的产品对象包含category字段,但错误发生在ProductDetail.jsx第32行,具体错误栈如下:
ProductDetail.jsx:32 Uncaught TypeError: Cannot read properties of undefined (reading 'categoryName') at ProductDetail (ProductDetail.jsx:32:1) at renderWithHooks (react-dom.development.js:16305:1) at mountIndeterminateComponent (react-dom.development.js:20074:1) at beginWork (react-dom.development.js:21587:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1) at invokeGuardedCallback (react-dom.development.js:4277:1) at beginWork$1 (react-dom.development.js:27451:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopSync (react-dom.development.js:26466:1) ProductDetail @ ProductDetail.jsx:32 renderWithHooks @ react-dom.development.js:16305 mountIndeterminateComponent @ react-dom.development.js:20074 beginWork @ react-dom.development.js:21587 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 performConcurrentWorkOnRoot @ react-dom.development.js:25738 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 ProductDetail.jsx:32 Uncaught TypeError: Cannot read properties of undefined (reading 'categoryName') at ProductDetail (ProductDetail.jsx:32:1) at renderWithHooks (react-dom.development.js:16305:1) at mountIndeterminateComponent (react-dom.development.js:20074:1) at beginWork (react-dom.development.js:21587:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1) at invokeGuardedCallback (react-dom.development.js:4277:1) at beginWork$1 (react-dom.development.js:27451:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopSync (react-dom.development.js:26466:1) ProductDetail @ ProductDetail.jsx:32 renderWithHooks @ react-dom.development.js:16305 mountIndeterminateComponent @ react-dom.development.js:20074 beginWork @ react-dom.development.js:21587 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 recoverFromConcurrentError @ react-dom.development.js:25850 performConcurrentWorkOnRoot @ react-dom.development.js:25750 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 react-dom.development.js:18687 The above error occurred in the <ProductDetail> component: at ProductDetail (http://localhost:3000/static/js/bundle.js:1033:62) at Routes (http://localhost:3000/static/js/bundle.js:46558:5) at div at GridColumn (http://localhost:3000/static/js/bundle.js:51973:24) at div at GridRow (http://localhost:3000/static/js/bundle.js:52083:24) at div at Grid (http://localhost:3000/static/js/bundle.js:51857:22) at div at Dashboard at div at Container (http://localhost:3000/static/js/bundle.js:53978:24) at div at App at Router (http://localhost:3000/static/js/bundle.js:46491:15) at BrowserRouter (http://localhost:3000/static/js/bundle.js:45300:5) Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries. logCapturedError @ react-dom.development.js:18687 update.callback @ react-dom.development.js:18720 callCallback @ react-dom.development.js:13923 commitUpdateQueue @ react-dom.development.js:13944 commitLayoutEffectOnFiber @ react-dom.development.js:23391 commitLayoutMountEffects_complete @ react-dom.development.js:24688 commitLayoutEffects_begin @ react-dom.development.js:24674 commitLayoutEffects @ react-dom.development.js:24612 commitRootImpl @ react-dom.development.js:26823 commitRoot @ react-dom.development.js:26682 finishConcurrentRender @ react-dom.development.js:25892 performConcurrentWorkOnRoot @ react-dom.development.js:25809 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 ProductDetail.jsx:32 Uncaught TypeError: Cannot read properties of undefined (reading 'categoryName') at ProductDetail (ProductDetail.jsx:32:1) at renderWithHooks (react-dom.development.js:16305:1) at mountIndeterminateComponent (react-dom.development.js:20074:1) at beginWork (react-dom.development.js:21587:1) at beginWork$1 (react-dom.development.js:27426:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopSync (react-dom.development.js:26466:1) at renderRootSync (react-dom.development.js:26434:1) at recoverFromConcurrentError (react-dom.development.js:25850:1) at performConcurrentWorkOnRoot (react-dom.development.js:25750:1)
解决方案
原因分析
页面刷新时,组件先执行初始渲染,此时product是初始值{},product.category为undefined,直接访问product.category.categoryName会触发报错。虽然API请求完成后会更新state,但初始渲染阶段的错误已经导致后续更新无法正常执行。
解决方法
有几种简单的方式可以避免这个问题:
可选链操作符(推荐)
在访问嵌套属性时使用?.,当某个属性为undefined或null时会直接返回undefined,不会抛出错误:<Card.Description> {product.category?.categoryName} </Card.Description>条件渲染
先判断product.category是否存在,再渲染对应的内容:<Card.Description> {product.category && product.category.categoryName} </Card.Description>设置初始state的默认结构
给product的初始值设置与API返回一致的结构,确保category是一个空对象:const [product, setProduct] = useState({ category: {} });加载状态判断
在API请求完成前显示加载提示,避免渲染未准备好的数据:const [product, setProduct] = useState(null); useEffect(() => { let productService = new ProductService(); productService.getProductById(id).then(result => setProduct(result.data.data)) }, []) if (!product) { return <div>加载中...</div>; } return ( // 原渲染内容 )
内容的提问来源于stack exchange,提问作者Burak Koc
相关产品推荐
相关产品推荐

