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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:25:31