React中如何用单一函数实现多卡片图片切换的优化方案
问题描述
我正在React应用中构建一个包含3张卡片的模块,每张卡片含一张图片,图片下方有2个按钮,点击可切换默认显示的图片。目前已实现功能,但多函数方案并非最佳实践。我创建了存储城市数据的JSON文件,基于城市名称和ID导入数据,现寻求高效方案:用单个函数处理所有卡片的图片切换,且支持后续添加更多图片或按钮。现有实现代码与数据结构如下:
// 原切换函数 const changeCity1 = () => { if(scenesData.paris[0].id == 1 && scenesData.paris[0].city == "paris") { imageRef1.current.src = scenesData.paris[0].imageURL1; } } const changeCity2 = () => { if(scenesData.paris[1].id == 2 && scenesData.paris[1].city == "paris") { imageRef1.current.src = scenesData.paris[1].imageURL2; } } const changeCity3 = () => { if(scenesData.tejMahal[0].id == 1 && scenesData.tejMahal[0].city == "tejMahal") { imageRef2.current.src = scenesData.tejMahal[0].imageURL1; } } const changeCity4 = () => { if(scenesData.tejMahal[1].id == 2 && scenesData.tejMahal[1].city == "tejMahal") { imageRef2.current.src = scenesData.tejMahal[1].imageURL2; } }
// 原卡片渲染代码 <div className="item-img"> <a className="imago wow"> <img ref={imageRef1} alt="image1" /> <div className="item-img-overlay"></div> </a> </div> <div className="cont"> <h6>paris</h6> <span> <div className="text-center"> <button className="nb butn light mt-30 full-width" onClick={changeColor1}> <span className="ls3 text-u">1</span> </button> <button className="nb butn light mt-30 full-width" onClick={changeColor2}> <span className="ls3 text-u">2</span> </button> </div> </span> </div> <div className="item-img"> <a className="imago wow"> <img ref={imageRef2} alt="image2" /> <div className="item-img-overlay"></div> </a> </div> <div className="cont"> <h6>Energies of Love</h6> <span> <div className="text-center"> <button className="nb butn light mt-30 full-width" onClick={changeColor3}> <span className="ls3 text-u">1</span> </button> <button className="nb butn light mt-30 full-width" onClick={changeColor4}> <span className="ls3 text-u">2</span> </button> </div> </span> </div>
// 原JSON数据 { "paris": [ { "id": 1, "city": "paris", "imageURL1": "www.example.com" }, { "id": 2, "city": "paris", "imageURL2": "www.example.com" } ], "tejMahal": [ { "id": 1, "city": "tejMahal", "imageURL1": "www.example.com" }, { "id": 2, "city": "tejMahal", "imageURL2": "www.example.com" } ] }
高效实现方案
当然存在更高效的实现方式,核心思路是用状态驱动UI、统一切换逻辑、动态渲染卡片,同时优化数据结构以支持扩展性:
1. 优化数据结构
将原JSON中每个城市的分散图片URL改为数组形式,支持任意数量的图片,后续新增图片只需追加数组元素:
{ "paris": [ { "id": 1, "city": "paris", "images": ["www.example.com/paris-1.jpg", "www.example.com/paris-2.jpg"] } ], "tejMahal": [ { "id": 1, "city": "tejMahal", "images": ["www.example.com/tej-1.jpg", "www.example.com/tej-2.jpg"] } ] }
2. React组件实现
import { useState } from 'react'; import scenesData from './your-data-file.json'; const CityCards = () => { // 初始化每个城市的默认图片索引(默认显示第一张) const [currentImageIndex, setCurrentImageIndex] = useState(() => { return Object.keys(scenesData).reduce((acc, cityName) => { acc[cityName] = 0; return acc; }, {}); }); // 通用切换函数:接收城市名和目标图片索引,统一处理所有卡片的图片切换 const handleImageSwitch = (cityName, targetIndex) => { setCurrentImageIndex(prevState => ({ ...prevState, [cityName]: targetIndex })); }; return ( <div className="cards-container"> {/* 动态遍历所有城市数据,生成卡片 */} {Object.entries(scenesData).map(([cityName, cityEntries]) => { const cityData = cityEntries[0]; // 取每个城市的主数据条目 const currentImgUrl = cityData.images[currentImageIndex[cityName]]; return ( <div key={cityName} className="card"> <div className="item-img"> <a className="imago wow"> <img src={currentImgUrl} alt={cityName} /> <div className="item-img-overlay"></div> </a> </div> <div className="cont"> <h6>{cityName}</h6> <span> <div className="text-center"> {/* 动态生成图片切换按钮,数量与该城市的图片数一致 */} {cityData.images.map((_, index) => ( <button key={index} className="nb butn light mt-30 full-width" onClick={() => handleImageSwitch(cityName, index)} > <span className="ls3 text-u">{index + 1}</span> </button> ))} </div> </span> </div> </div> ); })} </div> ); }; export default CityCards;
核心优化点
- 单一通用函数:
handleImageSwitch通过接收城市名和图片索引,统一处理所有卡片的切换逻辑,无需为每个按钮单独编写函数。 - 状态驱动UI:用React状态管理当前选中的图片索引,避免直接操作DOM,符合React的单向数据流模式,更易于维护。
- 动态渲染:通过遍历数据自动生成卡片和按钮,后续新增城市或图片只需修改JSON数据,无需修改组件渲染代码。
- 扩展性强:数据结构改为数组后,支持任意数量的图片,按钮会根据图片数量自动生成,无需额外开发。
内容的提问来源于stack exchange,提问作者David Jay
相关产品推荐
相关产品推荐

