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
相关产品推荐
相关产品推荐

