React中如何从Products组件修改Modal弹窗的显示状态
实现方案
核心思路是状态提升:把Modal的显隐状态从Modal组件内部移到父组件Products中管理,通过props向Modal传递状态和修改状态的方法。
第一步:改造Modal组件
移除Modal内部的open状态,改为从props接收控制参数:
import { Fragment } from "react"; import { Dialog, Transition } from "@headlessui/react"; import { CheckIcon } from "@heroicons/react/outline"; // 接收两个props:open(布尔值控制显隐)、onClose(关闭弹窗的回调方法) export default function Modal({ open, onClose }) { return ( <Transition.Root show={open} as={Fragment}> <Dialog as='div' className='fixed z-10 inset-0 overflow-y-auto' onClose={onClose} > <div className='flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0'> <Transition.Child as={Fragment} enter='ease-out duration-300' enterFrom='opacity-0' enterTo='opacity-100' leave='ease-in duration-200' leaveFrom='opacity-100' leaveTo='opacity-0' > <Dialog.Overlay className='fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity' /> </Transition.Child> <span className='hidden sm:inline-block sm:align-middle sm:h-screen' aria-hidden='true' > ​ </span> <Transition.Child as={Fragment} enter='ease-out duration-300' enterFrom='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95' enterTo='opacity-100 translate-y-0 sm:scale-100' leave='ease-in duration-200' leaveFrom='opacity-100 translate-y-0 sm:scale-100' leaveTo='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95' > <div className='inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6'> <div> <div className='mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100'> <CheckIcon className='h-6 w-6 text-green-600' aria-hidden='true' /> </div> <div className='mt-3 text-center sm:mt-5'> <Dialog.Title as='h3' className='text-lg leading-6 font-medium text-gray-900' > Payment successful </Dialog.Title> <div className='mt-2'> <p className='text-sm text-gray-500'> Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. </p> </div> </div> </div> <div className='mt-5 sm:mt-6'> <button type='button' className='inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm' onClick={onClose} > Go back to dashboard </button> </div> </div> </Transition.Child> </div> </Dialog> </Transition.Root> ); }
第二步:改造Products组件
在Products内部管理弹窗显隐状态,给商品项添加点击触发逻辑:
import { useState } from "react"; import Modal from "./Modal"; const products = [ { id: 1, name: "Organize Basic Set (Walnut)", price: "$149", rating: 5, reviewCount: 38, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-01.jpg", imageAlt: "TODO", href: "#", }, { id: 2, name: "Organize Pen Holder", price: "$15", rating: 5, reviewCount: 18, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-02.jpg", imageAlt: "TODO", href: "#", }, { id: 3, name: "Organize Sticky Note Holder", price: "$15", rating: 5, reviewCount: 14, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-03.jpg", imageAlt: "TODO", href: "#", }, { id: 4, name: "Organize Phone Holder", price: "$15", rating: 4, reviewCount: 21, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-04.jpg", imageAlt: "TODO", href: "#", }, { id: 5, name: "Organize Basic Set (Walnut)", price: "$149", rating: 5, reviewCount: 38, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-01.jpg", imageAlt: "TODO", href: "#", }, { id: 6, name: "Organize Pen Holder", price: "$15", rating: 5, reviewCount: 18, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-02.jpg", imageAlt: "TODO", href: "#", }, { id: 7, name: "Organize Sticky Note Holder", price: "$15", rating: 5, reviewCount: 14, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-03.jpg", imageAlt: "TODO", href: "#", }, { id: 8, name: "Organize Phone Holder", price: "$15", rating: 4, reviewCount: 21, imageSrc: "https://tailwindui.com/img/ecommerce-images/category-page-05-image-card-04.jpg", imageAlt: "TODO", href: "#", }, ]; function classNames(...classes) { return classes.filter(Boolean).join(" "); } export default function Products() { // 新增弹窗控制状态 const [modalOpen, setModalOpen] = useState(false); // 可选:如果需要展示点击的对应商品数据,可以再加一个状态存储当前点击的商品 // const [activeProduct, setActiveProduct] = useState(null); return ( <div className='bg-white' id='product-line'> {/* 引入Modal组件,传递状态和关闭方法 */} <Modal open={modalOpen} onClose={() => setModalOpen(false)} /> <div className='max-w-7xl mx-auto overflow-hidden sm:px-6 lg:px-8'> <h2 className='sr-only'>Products</h2> <h1 className='text-3xl font-extrabold text-blue-800 mt-10 sm:mt-20 mb-1 ml-4'> Product Line </h1> <div className='-mx-px border-l border-gray-200 grid grid-cols-2 sm:mx-0 md:grid-cols-3 lg:grid-cols-4'> {products.map((product) => ( <div key={product.id} className='group relative p-4 border-r border-b border-gray-200 sm:p-6' // 新增点击事件,点击商品打开弹窗,可同时存储当前商品 onClick={(e) => { e.preventDefault(); // setActiveProduct(product); setModalOpen(true); }} > <div className='rounded-lg overflow-hidden bg-gray-200 aspect-w-1 aspect-h-1 group-hover:opacity-75'> <img src={product.imageSrc} alt={product.imageAlt} className='w-full h-full object-center object-cover' /> </div> <div className='pt-10 pb-4 text-center'> <h3 className='text-sm font-medium text-gray-500'> <a href={product.href}> <span aria-hidden='true' className='absolute inset-0' /> {product.name} </a> </h3> </div> </div> ))} </div> </div> </div> ); }
调整说明
- 如果你需要在弹窗中展示当前点击的商品信息,可以把注释掉的activeProduct相关代码打开,然后把activeProduct作为props传给Modal组件即可。
- 代码中添加了
e.preventDefault()避免点击商品项触发a标签的默认跳转行为。
内容的提问来源于stack exchange,提问作者MadThad762
相关产品推荐
相关产品推荐

