React中fetch调用API获取万智牌数据渲染不一致问题求助
问题分析与解决方案
核心问题
- 异步处理逻辑错误:用
forEach循环执行异步fetch请求时,forEach不会等待异步操作完成,导致每个请求返回后立刻更新状态,造成状态分批次更新,渲染时只能显示当前已返回的卡牌。同时直接修改objects数组(objects.push(result)),没有创建新数组,React可能无法检测到状态变化。 - 渲染代码错误:每个条件分支都复用
scrydata[0]的图片地址和key,导致即使数组有多个元素,显示的都是第一张卡牌的重复内容,手动写7个条件也容易出错。 - Promise.all使用不当:你错误地将单个
response.json()包装进Promise.all,这完全无效,正确做法是把所有fetch请求的Promise收集起来,用Promise.all等待全部完成。
修复后的代码
1. 修正异步请求逻辑
const handleClick = async () => { // 分割卡牌列表并过滤空行,避免无效请求 const cardNames = textarea.split("\n").filter(name => name.trim()); // 限制最多7张卡牌 const limitedNames = cardNames.slice(0, 7); try { setIsLoading(true); // 生成所有fetch请求的Promise数组 const cardPromises = limitedNames.map(async (name) => { const response = await fetch( `https://api.scryfall.com/cards/named?exact=${encodeURIComponent(name.trim())}` ); if (!response.ok) { throw new Error(`获取卡牌失败: ${response.status}`); } return await response.json(); }); // 等待所有请求完成,拿到全部卡牌数据 const allCards = await Promise.all(cardPromises); // 打乱顺序(如果需要) const shuffledCards = [...allCards].sort(() => Math.random() - 0.5); // 一次性更新状态 setScrydata(shuffledCards); } catch (err) { console.error(err); } finally { setIsLoading(false); } };
2. 修正渲染逻辑
{scrydata.length > 0 && scrydata.map((card) => ( <img key={card.id} src={card.image_uris?.normal} alt={card.name} loading="lazy" // 可选,优化图片加载性能 /> ))}
关键改进点
- 批量异步处理:用
Promise.all等待所有请求完成后再更新状态,确保一次渲染所有卡牌。 - 不可变状态更新:创建新数组(
[...allCards])而非修改原数组,保证React能检测到状态变化。 - 简洁渲染:用
map循环替代重复的条件判断,自动渲染所有已获取的卡牌,且每个元素使用唯一的card.id作为key。 - 过滤空行:避免空字符串导致的无效API请求。
- 数量限制:提前截取最多7张卡牌,符合需求。
内容的提问来源于stack exchange,提问作者Robert Angberg
相关产品推荐
相关产品推荐

