React前端请求后端接口获取对应ID图片并渲染的解决方案求助
问题解决方案
原代码存在的核心问题
- 状态更新异步问题:
setItineraries是异步操作,执行完后立即遍历itineraries数组时,拿到的还是初始空数组,不会实际触发图片请求 - 状态更新不符合React规范:直接对
itineraryImage/itineraryCovImage执行push操作,没有调用对应的set方法,React无法感知状态变化,不会触发重渲染 - 关联关系错乱:异步请求的返回顺序不可控,直接按返回顺序push到数组,无法保证图片和对应的行程一一对应
- 冗余请求:后端图片接口本身支持直接访问,不需要用axios拉取图片数据再存储,属于多余操作
最优解决方案(无需额外请求)
你的后端图片接口可以直接作为img标签的src属性值使用,完全不需要额外发axios请求存储图片数据,修改逻辑如下:
- 删掉
itineraryImage、itineraryCovImage两个状态,以及useEffect里所有的图片axios请求逻辑 - 渲染CardImg时直接拼接接口地址作为src
- 点击查看行程时,把当前行程的id传入,用于拼接另一张图片的地址传递给组件
修改后的前端代码示例
import {Card , CardImg , CardBody , CardTitle , CardText , Button } from 'reactstrap'; import {useState} from 'react'; import { useEffect } from 'react'; import ItineraryContainer from 'components/ItineraryContainer'; import Backdrop from 'components/ItineraryBackdrop'; import styles from '../assets/css/Itinerary.module.css' import axios from 'axios'; function Itineraries(){ const [itineraries , setItineraries] = useState([]); const [currentItinerary, setCurrentItinerary] = useState(null); // 存储当前点击的行程 const [ItineraryisOpen , setItineraryOpen] = useState(false); useEffect(() =>{ axios.get("http://localhost:8070/itineraries/").then((res)=>{ setItineraries(res.data); }).catch((err)=>{ console.log(err); }) } , []); function ViewItinerary(itinerary){ setCurrentItinerary(itinerary); setItineraryOpen(true); } function ItineraryisClosed(){ setItineraryOpen(false); setCurrentItinerary(null); } return( <div> <div className = {styles.Packages}> <h3>Our Tours</h3> <br/><br/> <div className = {styles.container} > {itineraries.map((itinerary) =>( <Card key={itinerary._id} style = {{width: '20rem' , margin : '50px'}}> {/* 直接拼接封面图接口地址作为src */} <CardImg top src={`http://localhost:8070/itineraries/getCovImage/${itinerary._id}`} alt = "TourImage" /> <CardBody> <CardTitle>{itinerary.itineraryName}</CardTitle> <CardText> {itinerary.itineraryDesc}<br/><br/> <b>Itinerary Days : {itinerary.itineraryDays} </b> <b>Itinerary Class : {itinerary.itineraryClass}</b> <b>Price Per Adult : {itinerary.itineraryPriceAdult} </b> <br/> <b>Price per Child : {itinerary.itineraryPriceChild} </b> </CardText> {/* 点击时传入当前行程 */} <Button color = "primary" onClick = {() => ViewItinerary(itinerary)} >View Itinerary</Button> <Button color = "info" style = {{float : 'right'}}>Book Tour</Button> </CardBody> </Card> ))} </div> {/* 把行程的另一张图片地址作为prop传递 */} {ItineraryisOpen && currentItinerary ? <ItineraryContainer onCancel = {ItineraryisClosed} itineraryImage={`http://localhost:8070/itineraries/getImage/${currentItinerary._id}`} /> : null} {ItineraryisOpen && <Backdrop onCancel = {ItineraryisClosed}/>} </div> </div> ); } export default Itineraries;
特殊场景方案(需要预加载图片到本地)
如果业务需要提前把图片数据加载到前端存储,可以用对象存储图片,key为行程id保证关联正确:
// 状态修改为对象类型 const [imageMap, setImageMap] = useState({}); // useEffect里获取图片的逻辑 useEffect(() =>{ axios.get("http://localhost:8070/itineraries/").then(async (res) =>{ setItineraries(res.data); const tempMap = {}; // 等待所有图片请求完成 await Promise.all(res.data.map(async (i) => { const [imgRes, covRes] = await Promise.all([ axios.get(`http://localhost:8070/itineraries/getImage/${i._id}`, {responseType: 'blob'}), axios.get(`http://localhost:8070/itineraries/getCovImage/${i._id}`, {responseType: 'blob'}) ]); // 转成本地可访问的url tempMap[i._id] = { itineraryImage: URL.createObjectURL(imgRes.data), coverImage: URL.createObjectURL(covRes.data) } })) setImageMap(tempMap); }).catch((err)=>{ console.log(err); }) }, []); // 渲染时读取 <CardImg top src={imageMap[itinerary._id]?.coverImage || '默认图地址'} alt="TourImage" />
内容的提问来源于stack exchange,提问作者Shehan Silva
相关产品推荐
相关产品推荐

