You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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状态,具体修改如下:

  1. 在App组件里新增selectedObj状态:
const [selectedObj, setSelectedObj] = useState(null);
  1. 修改handleClick函数,设置选中对象而非覆盖原数据:
const handleClick = (obj) => {
  setSelectedObj(obj);
  setIsOpen(true);
};
  1. 给ModalOverlay传入selectedObj代替原来的data:
{isOpen && (
  <ModalOverlay
    isOpen={isOpen}
    obj={selectedObj}
    wholeData={wholeData}
    closeModal={closeModal}
  />
)}
  1. 可选:关闭模态框时清空选中对象,避免残留状态:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:14:54