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

Next.js项目中如何处理不同位置的多模态框触发问题

问题:点击项目卡片仅触发对应模态框而非全部弹出

我用Next.js搭建作品集网站,作品区的项目截图交替排列在屏幕左右两侧。点击项目截图时,希望在该截图区域中央弹出对应模态框(展示项目介绍、代码链接、技术栈等内容),但目前点击时所有项目的模态框都会被触发,我仅需要触发被点击项目的对应模态框。此前在其他项目中遇到过类似的全模态框触发问题,当时因所有模态框都在屏幕中央,通过箭头函数传递模态框内容数据解决了问题,但本次每个项目的模态框位置不同,我不知道该如何编写代码实现需求。

相关代码

WorkItemsDesk.js

export const WorkItemsDesk = () => {
  const [showModal, setShowModal] = useState(false);
  const togglePopUp = () => setShowModal(!showModal);

  return (
    <div className="flex flex-col my-[100px] gap-[100px] z-10 ">
      {projects.map((project) => (
        <div
          key={project.title}
          onClick={() => togglePopUp()} //This is so the modal closes when clicked outside
          className="w-[1000px] z-20 hover:cursor-pointer relative bg-no-repeat bg-center bg-fixed bg-clip-border bg-contain h-[470px] flex justify-center items-center odd:ml-auto hover:shadow-2xl transition-all duration-300 ease-in-out"
          style={{
            background: `linear-gradient(0deg, rgba(4, 4, 4, 0.65), rgba(4, 4, 4, 0.65)), url(${project.image});`,
            backgroundClip: "border-box",
            backgroundRepeat: "no-repeat",
            backgroundPosition: "center",
            backgroundSize: "cover",
          }}
        >
          <h1 className="font-montserrat-extrabold text-[40px] text-light-text">
            {project.title}
          </h1>
          <WorkModalDesk project={project} showModal={showModal} />
        </div>
      ))}
    </div>
  );
};

WorkModalDesk.js

export const WorkModalDesk = ({
  project: { p1, p2, tech, sourceCode, liveDemo, buttons },
  showModal,
}) => {
  return (
    <div
      className={
        showModal
          ? "flex pt-[24px] absolute px-[30px] pb-[34px] bg-dark rounded-lg flex-col gap-[40px] max-w-[450px] "
          : "hidden"
      }
    >
      <p className="text-[18px] text-light-text font-montserrat">
        {p1}
        <span className="block mt-[28px]">{p2}</span>
      </p>
      .
      .
      .
      .
    </div>
  );
};

解决方案

核心问题是所有模态框共用了同一个showModal状态,导致点击时全部触发。下面提供两种可行方案:

方案一:组件拆分(推荐)

把单个项目卡片和对应的模态框封装成独立组件,每个组件自己维护模态框的显示状态,彻底实现隔离。

1. 创建独立的ProjectCard组件

// ProjectCard.js
import { useState } from 'react';
import { WorkModalDesk } from './WorkModalDesk';

export const ProjectCard = ({ project }) => {
  const [showModal, setShowModal] = useState(false);
  
  const toggleModal = () => {
    setShowModal(!showModal);
  };

  return (
    <div
      onClick={toggleModal}
      className="w-[1000px] z-20 hover:cursor-pointer relative bg-no-repeat bg-center bg-fixed bg-clip-border bg-contain h-[470px] flex justify-center items-center odd:ml-auto hover:shadow-2xl transition-all duration-300 ease-in-out"
      style={{
        background: `linear-gradient(0deg, rgba(4, 4, 4, 0.65), rgba(4, 4, 4, 0.65)), url(${project.image})`,
        backgroundClip: "border-box",
        backgroundRepeat: "no-repeat",
        backgroundPosition: "center",
        backgroundSize: "cover",
      }}
    >
      <h1 className="font-montserrat-extrabold text-[40px] text-light-text">
        {project.title}
      </h1>
      <WorkModalDesk project={project} showModal={showModal} />
    </div>
  );
};

2. 修改WorkItemsDesk.js使用新组件

// WorkItemsDesk.js
import { ProjectCard } from './ProjectCard';

export const WorkItemsDesk = () => {
  return (
    <div className="flex flex-col my-[100px] gap-[100px] z-10">
      {projects.map((project) => (
        <ProjectCard key={project.title} project={project} />
      ))}
    </div>
  );
};

方案二:记录激活项目标识

如果不想拆分组件,可以用状态记录当前需要显示模态框的项目唯一标识(比如标题),渲染时判断匹配再显示。

修改WorkItemsDesk.js:

// WorkItemsDesk.js
import { useState } from 'react';
import { WorkModalDesk } from './WorkModalDesk';

export const WorkItemsDesk = () => {
  const [activeProjectTitle, setActiveProjectTitle] = useState(null);

  const toggleModal = (projectTitle) => {
    // 点击当前激活项目则关闭,否则激活该项目
    setActiveProjectTitle(prev => prev === projectTitle ? null : projectTitle);
  };

  return (
    <div className="flex flex-col my-[100px] gap-[100px] z-10">
      {projects.map((project) => (
        <div
          key={project.title}
          onClick={() => toggleModal(project.title)}
          className="w-[1000px] z-20 hover:cursor-pointer relative bg-no-repeat bg-center bg-fixed bg-clip-border bg-contain h-[470px] flex justify-center items-center odd:ml-auto hover:shadow-2xl transition-all duration-300 ease-in-out"
          style={{
            background: `linear-gradient(0deg, rgba(4, 4, 4, 0.65), rgba(4, 4, 4, 0.65)), url(${project.image})`,
            backgroundClip: "border-box",
            backgroundRepeat: "no-repeat",
            backgroundPosition: "center",
            backgroundSize: "cover",
          }}
        >
          <h1 className="font-montserrat-extrabold text-[40px] text-light-text">
            {project.title}
          </h1>
          {/* 仅匹配激活项目时显示模态框 */}
          <WorkModalDesk project={project} showModal={activeProjectTitle === project.title} />
        </div>
      ))}
    </div>
  );
};

注意事项

  • 方案一符合React组件化思想,每个卡片独立管理状态,后续维护更方便。
  • 两种方案都能保证模态框保持在对应项目卡片中央(因为模态框是卡片的子元素,absolute定位会相对于父卡片生效)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:05:40