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

React项目组件弹窗显示错误项目信息的问题求助

问题:点击项目弹窗始终显示最后一个项目信息

我在制作个人作品集的Projects组件,从外部文件导入项目数组,通过map方法动态渲染项目。点击项目按钮想弹出Modal展示对应项目的详情,但弹窗总是显示数组最后一个项目的信息,而非当前点击的项目。

当前代码

Projects组件

import { PlusCircle, Plus } from 'phosphor-react';
import { useState } from 'react';
import { data } from '../data/data.js'
import { Modal } from './Modal.js';

export const Projects = () => {
  const [openModal, setOpenModal] = useState(false);
  const project = data;

  return (
    <div name='projects' className='w-full md:h-screen text-white bg-dark'>
      <div className='max-w-[1000px] mx-auto p-4 flex flex-col justify-center w-full h-full'>
        <div className='pb-8'>
          <p className='text-4xl font-mono font-bold inline border-b-4 text-white border-cyan-200'>
            Projects
          </p>
          <p className='py-6'>// Check out some of my recent work</p>
          <p className='hidden md:flex'>Scroll to see more</p>
          <p className='md:hidden'>Click to see more</p>
        </div>

        {/* container for projects */}
        <div className="grid sm:grid-cols-2 md:grid-cols-3 gap-4">
          {/* Gird Item */}
          {project.map((item, index) => (
            <div
              key={index}
              style={{ backgroundImage: `url(${item.image})` }}
              className="shadow-lg shadow-[#040c16] group container rounded-md 
                        flex flex-col justify-center text-center  mx-auto content-div overflow-hidden"
            >
              {/* Hover effect for images */}
              <div className="opacity-0 group-hover:opacity-100 flex flex-col group justify-center align-middle items-center pt-6">
                <span className="text-2xl font bold text-white tracking-wider ">
                  {item.name}
                </span>
                <div className='flex justify-center items-center gap-1'>
                  <p className=''>Click here to see the description</p>
                  <PlusCircle size={22} className='cursor-pointer hover:rotate-180 duration-300' onClick={()=>{setOpenModal(true)}}/>
                </div>
                <div className="pt-5 pb-4 text-center ">
                  {/* eslint-disable-next-line */}
                  <a href={item.live} target="_blank">
                    <button
                      className="text-center rounded-lg px-4 py-3 m-2
                                 bg-white text-gray font-bold text-lg"
                    >
                      Demo
                    </button>
                  </a>
                  {/* eslint-disable-next-line */}
                  <a href={item.github} target="_blank">
                    <button
                      className="text-center rounded-lg px-4 py-3 m-2
                                 bg-white text-gray font-bold text-lg"
                    >
                      Code
                    </button>
                  </a>
                </div>
              </div>
              {openModal && <Modal setOpenModal={setOpenModal} item={item} index={index}/>}
              {openModal ? document.body.classList.add('active-modal') : document.body.classList.remove('active-modal')}
              <div className='flex items-start'>
                <div className='justify-center p-[5.1px] gap-1 bg-dark rounded-r-md flex ml-[-200px] group-hover:ml-0 duration-300'>
                  {
                    item.stack.map( stack => <img className='w-[25px]' src={stack} alt="" />)
                  }
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

Modal组件

import { X } from "phosphor-react"

export const Modal = ({setOpenModal, item, index}) => {
  return (
      // Modal Background
      <div key={index} className="w-full h-screen bg-[rgba(49,49,49,0.3)] fixed flex justify-center items-center top-0 left-0 right-0 bottom-0 text-black">
      {/* Modal Container */}
      <div className="w-[700px] h-[650px] rounded-[12px] bg-white shadow-md flex flex-col ">
          <div className="flex justify-end">
              <X size={25} className="text-[red] cursor-pointer fixed -ml-1 mt-1" onClick={()=>{setOpenModal(false)}}/>
          </div>
          {/* Tittle */}
          <div
            key={index}
            style={{ backgroundImage: `url(${item.image})` }}
            className="container 
                    flex flex-col justify-center text-center rounded-t-lg mx-auto bg-no-repeat bg-cover bg-center h-[100%] overflow-hidden"
        ></div>
          <div key={index} className="inline-block text-center mt-2 text-[22px] font-mono font-bold">
              <h1>{item.name}</h1>
          </div>
          {/* Body */}
          <div className="flex flex-col flex-[50%] justify-center items-center text-justify text-md px-12">
              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse et purus tempus, cursus ex vitae, pretium urna. Curabitur et lectus eget eros gravida fermentum et sit amet tellus. Quisque blandit suscipit nisi vitae volutpat. Curabitur mattis lectus mauris, ut scelerisque tortor blandit a. In hac habitasse platea dictumst. Vestibulum eget elit maximus, porttitor velit ac, ultrices est.</p>
          </div>
          {/* Footer */}
          <div className="flex flex-[50%] justify-center items-center mt-0">
            <a href={item.live} target="_blank">
                <button
                    className="text-center rounded-lg px-4 py-3 m-2
                            bg-cyan-800 text-white font-bold text-lg"
                >
                    Demo
                </button>
            </a>
                {/* eslint-disable-next-line */}
            <a href="https://github.com" target="_blank">
                <button
                    className="text-center rounded-lg px-4 py-3 m-2
                            bg-cyan-800 text-white font-bold text-lg"
                >
                    Code
                </button>
            </a>
          </div>
      </div>
  </div>
  )
}

问题原因

  1. 多Modal渲染冲突:在map循环中给每个项目都渲染了一个Modal组件,当openModal设为true时,所有项目对应的Modal都会被渲染。由于Modal是fixed定位,最后一个Modal会覆盖前面的,导致看起来总是显示最后一个项目的信息。
  2. 状态仅控制显示/隐藏:只用布尔值openModal无法区分当前点击的是哪个项目,没有存储选中项目的具体数据。

解决方案

修改状态逻辑,存储选中的项目对象,而不是单纯的布尔值,并且只渲染一个Modal组件:

修改后的Projects组件

import { PlusCircle, Plus } from 'phosphor-react';
import { useState, useEffect } from 'react';
import { data } from '../data/data.js'
import { Modal } from './Modal.js';

export const Projects = () => {
  // 改为存储选中的项目,初始为null
  const [selectedProject, setSelectedProject] = useState(null);
  const project = data;

  // 用useEffect处理body的class,避免每次渲染都执行
  useEffect(() => {
    if (selectedProject) {
      document.body.classList.add('active-modal');
    } else {
      document.body.classList.remove('active-modal');
    }
    // 清理函数,组件卸载时移除class
    return () => {
      document.body.classList.remove('active-modal');
    };
  }, [selectedProject]);

  return (
    <div name='projects' className='w-full md:h-screen text-white bg-dark'>
      <div className='max-w-[1000px] mx-auto p-4 flex flex-col justify-center w-full h-full'>
        <div className='pb-8'>
          <p className='text-4xl font-mono font-bold inline border-b-4 text-white border-cyan-200'>
            Projects
          </p>
          <p className='py-6'>// Check out some of my recent work</p>
          <p className='hidden md:flex'>Scroll to see more</p>
          <p className='md:hidden'>Click to see more</p>
        </div>

        {/* container for projects */}
        <div className="grid sm:grid-cols-2 md:grid-cols-3 gap-4">
          {/* Gird Item */}
          {project.map((item, index) => (
            <div
              key={index}
              style={{ backgroundImage: `url(${item.image})` }}
              className="shadow-lg shadow-[#040c16] group container rounded-md 
                        flex flex-col justify-center text-center  mx-auto content-div overflow-hidden"
            >
              {/* Hover effect for images */}
              <div className="opacity-0 group-hover:opacity-100 flex flex-col group justify-center align-middle items-center pt-6">
                <span className="text-2xl font bold text-white tracking-wider ">
                  {item.name}
                </span>
                <div className='flex justify-center items-center gap-1'>
                  <p className=''>Click here to see the description</p>
                  {/* 点击时将当前项目存入状态 */}
                  <PlusCircle size={22} className='cursor-pointer hover:rotate-180 duration-300' onClick={()=>{setSelectedProject(item)}}/>
                </div>
                <div className="pt-5 pb-4 text-center ">
                  {/* eslint-disable-next-line */}
                  <a href={item.live} target="_blank">
                    <button
                      className="text-center rounded-lg px-4 py-3 m-2
                                 bg-white text-gray font-bold text-lg"
                    >
                      Demo
                    </button>
                  </a>
                  {/* eslint-disable-next-line */}
                  <a href={item.github} target="_blank">
                    <button
                      className="text-center rounded-lg px-4 py-3 m-2
                                 bg-white text-gray font-bold text-lg"
                    >
                      Code
                    </button>
                  </a>
                </div>
              </div>
              <div className='flex items-start'>
                <div className='justify-center p-[5.1px] gap-1 bg-dark rounded-r-md flex ml-[-200px] group-hover:ml-0 duration-300'>
                  {
                    item.stack.map( stack => <img className='w-[25px]' src={stack} alt="" />)
                  }
                </div>
              </div>
            </div>
          ))}
        </div>
        {/* 只渲染一个Modal,放在循环外面 */}
        {selectedProject && <Modal setSelectedProject={setSelectedProject} item={selectedProject}/>}
      </div>
    </div>
  );
};

修改后的Modal组件

import { X } from "phosphor-react"

export const Modal = ({setSelectedProject, item}) => {
  return (
      // Modal Background
      <div className="w-full h-screen bg-[rgba(49,49,49,0.3)] fixed flex justify-center items-center top-0 left-0 right-0 bottom-0 text-black">
      {/* Modal Container */}
      <div className="w-[700px] h-[650px] rounded-[12px] bg-white shadow-md flex flex-col ">
          <div className="flex justify-end">
              {/* 关闭时将选中项目设为null */}
              <X size={25} className="text-[red] cursor-pointer fixed -ml-1 mt-1" onClick={()=>{setSelectedProject(null)}}/>
          </div>
          {/* Tittle */}
          <div
            style={{ backgroundImage: `url(${item.image})` }}
            className="container 
                    flex flex-col justify-center text-center rounded-t-lg mx-auto bg-no-repeat bg-cover bg-center h-[100%] overflow-hidden"
        ></div>
          <div className="inline-block text-center mt-2 text-[22px] font-mono font-bold">
              <h1>{item.name}</h1>
          </div>
          {/* Body */}
          <div className="flex flex-col flex-[50%] justify-center items-center text-justify text-md px-12">
              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse et purus tempus, cursus ex vitae, pretium urna. Curabitur et lectus eget eros gravida fermentum et sit amet tellus. Quisque blandit suscipit nisi vitae volutpat. Curabitur mattis lectus mauris, ut scelerisque tortor blandit a. In hac habitasse platea dictumst. Vestibulum eget elit maximus, porttitor velit ac, ultrices est.</p>
          </div>
          {/* Footer */}
          <div className="flex flex-[50%] justify-center items-center mt-0">
            <a href={item.live} target="_blank">
                <button
                    className="text-center rounded-lg px-4 py-3 m-2
                            bg-cyan-800 text-white font-bold text-lg"
                >
                    Demo
                </button>
            </a>
                {/* eslint-disable-next-line */}
            <a href={item.github} target="_blank">
                <button
                    className="text-center rounded-lg px-4 py-3 m-2
                            bg-cyan-800 text-white font-bold text-lg"
                >
                    Code
                </button>
            </a>
          </div>
      </div>
  </div>
  )
}

关键修改点

  1. 状态变更:将openModal布尔值改为selectedProject对象,存储当前点击的项目数据,明确区分选中项。
  2. 单一Modal渲染:把Modal组件移到map循环外部,避免重复渲染多个Modal,解决覆盖问题。
  3. useEffect处理body类:用useEffect监听selectedProject变化,统一处理body的active-modal类,避免每次渲染都执行DOM操作。
  4. Modal传参调整:修改Modal的props,传入setSelectedProject用于关闭弹窗,同时修正了Code按钮的链接为item.github(原代码写死了github地址)。

内容的提问来源于stack exchange,提问作者Carlos Curcino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:15:43