React项目问题:无法展示API返回图片数组的首张图片
酒店图片展示问题及Modal画廊实现方案
当前问题
- 可通过CSS展示所有图片并大致排列位置,但酒店有多张图片时,无法仅显示首张图片
- 最终目标:点击图片弹出Modal展示图片画廊,目前卡在图片展示控制环节
现有代码
import React, { useState, useEffect } from "react"; import "./Hotel.css"; const URL = "https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG"; const Hotel = () => { const [hotel, setHotel] = useState([]); useEffect(() => { hotels(); }, []); const hotels = async () => { const response = await fetch(URL); setHotel(await response.json()); }; // 按星级过滤酒店 const filterHotels = (e) => { const starRating = e.target.value; const filteredHotels = hotel.filter( (hotel) => hotel.starRating === starRating ); setHotel(filteredHotels); }; const [filteredHotels, setFilteredHotels] = useState([]); useEffect(() => { setFilteredHotels(hotel); }, [hotel]); return ( <div> <div className="selection-filter"> <label for="filter">Filter by star rating: </label> <select onChange={filterHotels}> <option value="0">All</option> <option value="1">1 Star</option> <option value="2">2 Star</option> <option value="3">3 Star</option> <option value="4">4 Star</option> <option value="5">5 Star</option> </select> </div> {hotel.map((data) => { return ( <div> <div className="list-group-item hotel-area" key={data.id}> <div className="hotel-name">{data.name}</div> {data.images.map((image) => ( <img className="hotel-image" src={data.images[0].url} alt={image.alt} /> ))} <div className="hotel-address">{data.address1}</div> <div className="hotel-address">{data.address2}</div> <div className="star-rating fas fa-star">{data.starRating}</div> <hr /> </div> </div> ); })} </div> ); }; export default Hotel;
解决方案
1. 仅显示首张图片
问题出在data.images.map()遍历所有图片,且每张都引用首张图片地址,导致重复渲染。直接判断图片数组长度后渲染首张即可:
{data.images.length > 0 && ( <img className="hotel-image" src={data.images[0].url} alt={data.images[0].alt || "酒店图片"} onClick={() => openImageModal(data.images)} // 绑定点击事件传递所有图片 /> )}
2. 实现图片Modal画廊
添加Modal相关状态
const [isModalOpen, setIsModalOpen] = useState(false); const [currentImages, setCurrentImages] = useState([]); const [currentImageIndex, setCurrentImageIndex] = useState(0); // 打开Modal const openImageModal = (images) => { setCurrentImages(images); setCurrentImageIndex(0); setIsModalOpen(true); }; // 关闭Modal const closeImageModal = () => { setIsModalOpen(false); }; // 切换图片 const navigateImage = (direction) => { const newIndex = currentImageIndex + direction; if (newIndex < 0) { setCurrentImageIndex(currentImages.length - 1); } else if (newIndex >= currentImages.length) { setCurrentImageIndex(0); } else { setCurrentImageIndex(newIndex); } };
在JSX中添加Modal组件
{isModalOpen && ( <div className="image-modal-overlay" onClick={closeImageModal}> <div className="image-modal-content" onClick={(e) => e.stopPropagation()}> <button className="modal-close-btn" onClick={closeImageModal}>×</button> <button className="modal-nav-btn prev-btn" onClick={() => navigateImage(-1)}>←</button> <img src={currentImages[currentImageIndex].url} alt={currentImages[currentImageIndex].alt || "画廊图片"} className="modal-image" /> <button className="modal-nav-btn next-btn" onClick={() => navigateImage(1)}>→</button> <div className="modal-image-count"> {currentImageIndex + 1} / {currentImages.length} </div> </div> </div> )}
补充Modal样式(Hotel.css)
/* Modal遮罩层 */ .image-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 1000; } /* Modal内容容器 */ .image-modal-content { position: relative; max-width: 90%; max-height: 90%; } /* Modal图片 */ .modal-image { width: 100%; height: auto; border-radius: 8px; } /* 关闭按钮 */ .modal-close-btn { position: absolute; top: -40px; right: 0; background: none; border: none; color: white; font-size: 32px; cursor: pointer; } /* 导航按钮 */ .modal-nav-btn { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255, 255, 255, 0.3); border: none; color: white; font-size: 24px; padding: 10px 15px; cursor: pointer; border-radius: 50%; } .prev-btn { left: -50px; } .next-btn { right: -50px; } /* 图片计数 */ .modal-image-count { position: absolute; bottom: -30px; left: 50%; transform: translateX(-50%); color: white; font-size: 16px; }
3. 修复过滤功能bug
原过滤逻辑无法匹配"All"选项,且直接修改原始酒店数据导致无法恢复全部数据,调整如下:
// 拆分状态,保留原始数据 const [allHotels, setAllHotels] = useState([]); const [filteredHotels, setFilteredHotels] = useState([]); useEffect(() => { const fetchHotels = async () => { const response = await fetch(URL); const data = await response.json(); setAllHotels(data); setFilteredHotels(data); }; fetchHotels(); }, []); // 修正过滤逻辑 const filterHotels = (e) => { const starRating = e.target.value; if (starRating === "0") { setFilteredHotels(allHotels); } else { const filtered = allHotels.filter(hotel => hotel.starRating === Number(starRating)); setFilteredHotels(filtered); } }; // 渲染时使用filteredHotels {filteredHotels.map((data) => { ... })}
内容的提问来源于stack exchange,提问作者SCOCB
相关产品推荐
相关产品推荐

