React中打开模态框时如何保留API渲染的背景元素?
问题:打开模态框时如何保留背景中API渲染的元素?
我正在尝试创建一个展示API返回项目预览的模态框,但每当点击API渲染的元素打开模态框时,背景中所有由API渲染的元素都会消失。相关代码如下:
App.jsx
import { useEffect, useState } from 'react'; import { Card } from './Card'; import png from '../src/assets/folder-icon.png'; import ModalOverlay from './ModalOverlay'; function App() { const [data, setData] = useState([]); const [wholeData, setWholeData] = useState([]); const [isOpen, setIsOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false); useEffect(() => { fetch('http://localhost:3000/data') .then((res) => res.json()) .then((data) => { setData(data); setWholeData(data); }); }, []); const handleClick = (obj) => { setData(obj); setIsOpen(true); }; const closeModal = (obj) => { setIsOpen(false); }; const CardList = () => { return ( <div className='model'> {data?.metadata?.map((obj) => ( <div key={obj.objectId}> <div className='button modelCard' onClick={() => handleClick(obj)} key={obj.objectId} > <img className='card-img' src={png} alt='png' /> </div> </div> ))} </div> ); }; return ( <div className='App'> {data && <CardList />} {isOpen && ( <ModalOverlay isOpen={isOpen} obj={data} wholeData={wholeData} closeModal={closeModal} /> )} </div> ); } export default App;
ModalOverlay.jsx
import React, { useState, useEffect } from 'react'; const ModalOverlay = ({ obj, wholeData, isOpen, closeModal }) => { return ( <div className='modal-overlay'> <div className='modal-content' onClick={(e) => e.stopPropagation()}> <div className='card-img'> <h2 className='card-title'> Object Name: {obj.properties.General.Name} </h2> <img className='object-image' src={`data:image/jpeg;base64,${wholeData.thumbnail}`} alt='object-image' /> <button className='view-360-btn'>Go 360 View</button> <button onClick={closeModal}>Kapat</button> </div> </div> </div> ); }; export default ModalOverlay;
index.css
:root { line-height: 1.5; color-scheme: light dark; color: black; background-color: #333; } body { font-family: 'Karla', sans-serif; display: flex; min-width: 320px; min-height: 100vh; } ul { list-style-type: none; padding: 0; } img { max-width: 100%; } #root { height: 100vh; width: 100vw; } /* CARD LIST */ .model { display: grid; justify-content: center; align-content: center; text-align: center; padding: 2em; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 2.5em; } .modelCard { display: flex; flex-direction: column; justify-content: center; border-radius: 10px; cursor: pointer; background-color: #f3f3f3; transition: 100ms; } .modelCard:hover { transform: translateY(-1.3em); } /* CARD DETAILS */ .card { min-height: 100vh; display: flex; justify-content: center; } .card-details { color: white; background-color: #0c2046; display: flex; flex-direction: column; padding: 1.2em; } .card-title { text-align: center; color: white; margin-top: 0; margin-bottom: 0.5em; } .card-img { display: flex; flex-direction: column; } .view-360-btn { padding: 1em 2em; border: none; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; background-color: lightgreen; color: black; font-size: 0.9em; cursor: pointer; font-weight: bold; } .view-360-btn:hover { background-color: #6ff36f; } .object-detail-image { border-top-right-radius: 5px; border-top-left-radius: 5px; width: 450px; height: 450px; } .detail-section { display: flex; flex-direction: column; text-align: center; } .detail-title { margin-bottom: 0; } .detail { display: flex; justify-content: space-between; } .detail-name { font-size: 1.2em; font-weight: bold; } .detail-value { font-size: 1.1em; font-weight: 500; } /* CARD PREVIEW */ /* Modal açıldığında arka planı karartmak için modal-overlay kullanıyoruz */ .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba( 0, 0, 0, 0.5 ); /* Karartma için yarı saydam bir arka plan */ display: flex; justify-content: center; align-items: center; animation: fadeIn 100ms ease-in; /* 100ms açılış animasyonu */ } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .App { min-height: 100vh; min-width: 320px; }
解决方案
问题核心是handleClick函数里错误地覆盖了存储完整API数据的data状态:你把原本包含metadata列表的data替换成了单个选中对象,导致CardList组件找不到data.metadata,自然渲染不出背景卡片。
只需要新增一个专门存储选中对象的状态,不要修改原有的data状态,具体修改如下:
- 在App组件里新增
selectedObj状态:
const [selectedObj, setSelectedObj] = useState(null);
- 修改
handleClick函数,设置选中对象而非覆盖原数据:
const handleClick = (obj) => { setSelectedObj(obj); setIsOpen(true); };
- 给ModalOverlay传入
selectedObj代替原来的data:
{isOpen && ( <ModalOverlay isOpen={isOpen} obj={selectedObj} wholeData={wholeData} closeModal={closeModal} /> )}
- 可选:关闭模态框时清空选中对象,避免残留状态:
const closeModal = () => { setIsOpen(false); setSelectedObj(null); };
修改后的完整App.jsx代码:
import { useEffect, useState } from 'react'; import { Card } from './Card'; import png from '../src/assets/folder-icon.png'; import ModalOverlay from './ModalOverlay'; function App() { const [data, setData] = useState([]); const [wholeData, setWholeData] = useState([]); const [isOpen, setIsOpen] = useState(false); const [selectedObj, setSelectedObj] = useState(null); useEffect(() => { fetch('http://localhost:3000/data') .then((res) => res.json()) .then((data) => { setData(data); setWholeData(data); }); }, []); const handleClick = (obj) => { setSelectedObj(obj); setIsOpen(true); }; const closeModal = () => { setIsOpen(false); setSelectedObj(null); }; const CardList = () => { return ( <div className='model'> {data?.metadata?.map((obj) => ( <div key={obj.objectId}> <div className='button modelCard' onClick={() => handleClick(obj)} key={obj.objectId} > <img className='card-img' src={png} alt='png' /> </div> </div> ))} </div> ); }; return ( <div className='App'> {data && <CardList />} {isOpen && ( <ModalOverlay isOpen={isOpen} obj={selectedObj} wholeData={wholeData} closeModal={closeModal} /> )} </div> ); } export default App;
修改后,打开模态框时背景的卡片列表会正常保留,因为存储列表数据的data状态没有被改动。
内容的提问来源于stack exchange,提问作者Ender Çelik
相关产品推荐
相关产品推荐

