使用react-bootstrap/Modal组件无法获取正确值的问题
问题:React-Bootstrap Modal点击后总是显示最后一条数据
开发React应用时,使用Button、Table等组件正常,但使用react-bootstrap/Modal时出现异常:点击任意按钮,Modal始终显示JSON列表中最后一个对象的内容。
服务器端JSON数据
let jsonlist = [ { Title: "first title", Listofitems: ["Item 01", "Item 02", "Item 03"] }, { Title: "second title", Listofitems: ["Item 04", "Item 05", "Item 06"] } ]
客户端处理代码
import { React, useEffect, useState, useRef } from 'react'; import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal'; const ModalList = ({ socket }) => { const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); const [lists, setlist] = useState([]); const fetchFact = () => { fetch("http://myapi/getList") .then((response) => response.json()) .then((data) => setlist(data)); } useEffect(() => { fetchFact() }, []); return ( <div className='container'> {lists.map(singleobject => ( <div> <div>{singleobject.title}</div> <div>{singleobject.listofelement.join(', ')}</div> <div> <Button variant="primary" onClick={handleShow}> {singleobject.title} </Button> <Modal show={show} onHide={handleClose}> <Modal.Header closeButton> <Modal.Title>{singleobject.title}</Modal.Title> </Modal.Header> <Modal.Body>{singleobject.listofelement.join(', ')}</Modal.Body> <Modal.Footer> <Button variant="secondary" onClick={handleClose}> Close </Button> <Button variant="primary" onClick={handleClose}> Save </Button> </Modal.Footer> </Modal></div> </div> ))} </div> ); } export default ModalList;
解决方案
问题根源
- 共用状态导致同步显示:所有Modal绑定同一个
show状态,点击任意按钮会触发所有Modal渲染,最后一个Modal会覆盖前面的,看起来只显示最后一条数据。 - 闭包变量捕获异常:循环中的
singleobject在闭包中被后续迭代覆盖,Modal最终渲染时,所有引用都指向最后一个元素。 - 字段名不匹配:服务器返回字段是
Title和Listofitems,但客户端用了title和listofelement,导致数据无法正确读取。
修复方法
方法一:用状态存储当前选中对象
只维护一个Modal,点击按钮时记录当前选中的对象,Modal根据该对象渲染内容:
import { React, useEffect, useState } from 'react'; import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal'; const ModalList = ({ socket }) => { const [show, setShow] = useState(false); const [currentItem, setCurrentItem] = useState(null); const [lists, setlist] = useState([]); const handleClose = () => { setShow(false); setCurrentItem(null); }; const handleShow = (item) => { setCurrentItem(item); setShow(true); }; const fetchFact = () => { fetch("http://myapi/getList") .then((response) => response.json()) .then((data) => setlist(data)); }; useEffect(() => { fetchFact(); }, []); return ( <div className='container'> {lists.map((singleobject, index) => ( <div key={index}> <div>{singleobject.Title}</div> <div>{singleobject.Listofitems.join(', ')}</div> <div> <Button variant="primary" onClick={() => handleShow(singleobject)}> {singleobject.Title} </Button> </div> </div> ))} {currentItem && ( <Modal show={show} onHide={handleClose}> <Modal.Header closeButton> <Modal.Title>{currentItem.Title}</Modal.Title> </Modal.Header> <Modal.Body>{currentItem.Listofitems.join(', ')}</Modal.Body> <Modal.Footer> <Button variant="secondary" onClick={handleClose}> Close </Button> <Button variant="primary" onClick={handleClose}> Save </Button> </Modal.Footer> </Modal> )} </div> ); }; export default ModalList;
方法二:将Modal抽成独立组件
把按钮和对应Modal封装成独立组件,每个组件维护自己的show状态,避免闭包问题:
import { React, useEffect, useState } from 'react'; import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal'; const ItemModal = ({ item }) => { const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); return ( <div> <div>{item.Title}</div> <div>{item.Listofitems.join(', ')}</div> <Button variant="primary" onClick={handleShow}> {item.Title} </Button> <Modal show={show} onHide={handleClose}> <Modal.Header closeButton> <Modal.Title>{item.Title}</Modal.Title> </Modal.Header> <Modal.Body>{item.Listofitems.join(', ')}</Modal.Body> <Modal.Footer> <Button variant="secondary" onClick={handleClose}> Close </Button> <Button variant="primary" onClick={handleClose}> Save </Button> </Modal.Footer> </Modal> </div> ); }; const ModalList = ({ socket }) => { const [lists, setlist] = useState([]); const fetchFact = () => { fetch("http://myapi/getList") .then((response) => response.json()) .then((data) => setlist(data)); }; useEffect(() => { fetchFact(); }, []); return ( <div className='container'> {lists.map((item, index) => ( <ItemModal key={index} item={item} /> ))} </div> ); }; export default ModalList;
额外注意点
- 循环渲染必须添加
key属性,避免React渲染异常。 - 统一字段名:确保客户端使用的字段和服务器返回一致,或在数据获取时做格式转换。
内容的提问来源于stack exchange,提问作者qwertzui
相关产品推荐
相关产品推荐

