React拆分Bootstrap Modal组件后出现TypeError: Cannot read properties of undefined (reading 'name')错误求助
解决React Bootstrap Modal拆分后出现的TypeError: Cannot read properties of undefined (reading 'name')问题
咱们来一步步拆解你遇到的问题,然后给出修复方案:
错误原因分析
- 重复渲染Modal组件:你在表格的每一行都渲染了一个
DetailModal,这意味着页面上同时存在10个Modal实例,它们共享同一个show状态。当组件初始化时,selectedItem是空对象{},所有Modal都会尝试访问selectedItem.name,自然会触发"读取undefined的name属性"错误。 - props传递错误:
DetailModal里试图使用list这个props,但你在父组件调用它时根本没传递这个参数,导致点击按钮时handleShow(e, list)里的list是undefined,最终selectedItem被设置为undefined,进一步加剧了错误。 - 初始状态无防御性处理:
selectedItem初始值是空对象,当Modal尝试渲染标题时,直接访问selectedItem.name没有做任何空值判断。
修复后的代码
1. 修改ModalPractice.js
把Modal组件移到表格外面(只渲染一次),表格行里只保留触发按钮,直接调用handleShow传递当前条目:
import React, { useState } from 'react'; import { Table, Button } from 'react-bootstrap'; import 'bootstrap/dist/css/bootstrap.min.css'; import { DetailModal } from './DetailModal'; const ModalPractice = () => { const [show, setShow] = useState(false); const [selectedItem, setSelectedItem] = useState(null); // 改为null,更直观表示无选中项 const handleClose = () => { setShow(false); setSelectedItem(null); }; const handleShow = (item) => { setSelectedItem(item); setShow(true); }; const food = [ { id: 1, name: 'rice', category: 'grain', image: 'images/rice.jpg' }, { id: 2, name: 'beans', category: 'grain and protein', image: 'images/beans.jpg' }, { id: 3, name: 'amala', category: 'swallow', image: 'images/amala.jpg' }, { id: 4, name: 'Oat', category: 'cereals', image: 'images/oat.jpg' }, { id: 5, name: 'coke', category: 'soft drink', image: 'images/coke.jpg' }, { id: 6, name: 'banana', category: 'fruit', image: 'images/banana.jpg' }, { id: 7, name: 'okra', category: 'vegetable', image: 'images/okra.jpg' }, { id: 8, name: 'yam', category: 'tuber', image: 'images/yam.jpg' }, { id: 9, name: 'palm oil', category: 'fat', image: 'images/palmoil.jpg' }, { id: 10, name: 'orange', category: 'fruit', image: 'images/orange.jpg' }, ]; return ( <div> {/* 只渲染一个Modal,放在表格外面 */} <DetailModal show={show} handleClose={handleClose} selectedItem={selectedItem} /> <Table striped bordered hover> <thead> <tr> <th>#</th> <th>Food Name</th> <th>Food Category</th> <th>Image</th> <th>Action</th> {/* 新增Action列 */} </tr> </thead> <tbody> {food.map((item) => ( <tr className='align-middle' key={item.id}> <td>{item.id}</td> <td>{item.name}</td> <td>{item.category}</td> <td> <img alt={item.name} src={item.image} width='100' height='100' /> </td> <td> {/* 直接在表格行里放按钮,调用handleShow传递当前item */} <Button variant='primary' onClick={() => handleShow(item)}> Detail </Button> </td> </tr> ))} </tbody> </Table> </div> ); }; export default ModalPractice;
2. 修改DetailModal.js
简化组件职责,只负责渲染Modal内容,添加空值判断避免报错:
import React from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; import { Button, Modal } from 'react-bootstrap'; export const DetailModal = (props) => { const { selectedItem, show, handleClose } = props; // 如果没有选中项,直接返回null,避免渲染空Modal if (!selectedItem) return null; return ( <Modal show={show} onHide={handleClose}> <Modal.Header closeButton> <Modal.Title>{selectedItem.name}</Modal.Title> </Modal.Header> <Modal.Body>{selectedItem.category}</Modal.Body> <Modal.Footer> <Button variant='secondary' onClick={handleClose}> Close </Button> <Button variant='primary' onClick={handleClose}> Save Changes </Button> </Modal.Footer> </Modal> ); };
关键优化点
- 单一Modal实例:只渲染一个Modal组件,避免重复渲染带来的性能问题和状态冲突。
- 空值防御:在
DetailModal里判断selectedItem是否存在,不存在时不渲染Modal内容,或者也可以使用可选链操作符selectedItem?.name替代直接访问。 - 清晰的职责划分:父组件负责管理列表和选中状态,子组件只负责渲染Modal内容,逻辑更清晰易维护。
内容的提问来源于stack exchange,提问作者Abubakar Oluyinka
相关产品推荐
相关产品推荐

