React JS如何捕获HTTP请求返回对象缺失指定数组的异常
问题修复方案
崩溃原因
页面崩溃的核心原因是未对接口可能缺失的字段做空值兜底:TMDB的watch/providers接口返回结构是动态的,当对应地区没有某类播放渠道时,返回的地区数据对象不会携带flatrate(订阅播放)、rent(租赁)字段,此时直接对undefined值调用数组的.map()方法会抛出JS运行时错误,中断React渲染流程导致整个页面崩溃。
原有代码还存在几个隐藏问题:
- 请求错误捕获仅覆盖网络请求、JSON解析阶段,未覆盖接口返回字段缺失的场景
- 列表项使用
Math.random()作为key,每次重渲染都会生成新值,导致DOM重复创建、渲染异常 - img标签的src属性存在语法错误,多写了多余的点号
- 初始状态
streamingData为null,字段读取时存在额外的空值风险 - 混用
async/await和.then()链式调用,错误捕获范围不全
具体修复逻辑
- 初始化
streamingData时就给好默认空结构,所有数组字段默认赋值为空数组,从根源避免读取undefined的问题 - 接口返回后逐层判断字段是否存在,对数组类型做类型校验,不存在/类型不对时直接兜底为空数组
- 改用接口返回的流媒体平台唯一标识
provider_id作为列表key,保证渲染稳定性 - 修正img标签语法,补充alt属性提升可访问性
- 增加空状态提示,当某类渠道不存在时给用户明确反馈,避免空白区域
- 用
try/catch统一处理所有请求、解析、数据处理阶段的错误,出错时重置为空状态
修复后完整代码
import React, { useState } from 'react' import classes from './Streaming.module.css' const Streaming = (props) => { const [streamingOn, setStreamingOn] = useState(false) // 初始化时直接给默认空结构,避免初始null导致的读取错误 const [streamingData, setStreamingData] = useState({ flatrate: [], rent: [], link: '' }) async function receiveStreaming() { try { const res = await fetch(`https://api.themoviedb.org/3/movie/${props.movie}/watch/providers?api_key=35135143f12a5c114d5d09d17dfcea12`) const result = await res.json() // 兜底:US地区数据不存在时使用默认空结构 const usRegionData = result?.results?.US || { flatrate: [], rent: [], link: '' } // 对每个字段做类型校验,非数组/不存在时兜底为空数组 setStreamingData({ flatrate: Array.isArray(usRegionData.flatrate) ? usRegionData.flatrate : [], rent: Array.isArray(usRegionData.rent) ? usRegionData.rent : [], link: usRegionData.link || '' }) setStreamingOn(true) } catch (error) { console.error("获取流媒体信息失败: ", error) // 出错时重置为空状态,避免渲染报错 setStreamingData({ flatrate: [], rent: [], link: '' }) setStreamingOn(false) } } const displayStreaming = (data) => { const { flatrate: streamList, rent: rentList, link } = data // 用接口返回的唯一provider_id作为列表key,保证渲染稳定 const streamItems = streamList.map(provider => <li key={provider.provider_id}> <a href={link}> <img className={classes.logoimg} src={`https://image.tmdb.org/t/p/w500/${provider.logo_path}`} alt={provider.provider_name} /> </a> </li> ) const rentItems = rentList.map(provider => <li key={provider.provider_id}> <a href={link}> <img className={classes.logoimg} src={`https://image.tmdb.org/t/p/w500/${provider.logo_path}`} alt={provider.provider_name} /> </a> </li> ) return ( <> <p>订阅播放平台</p> {streamItems.length ? ( <ul className={classes.logolist}>{streamItems}</ul> ) : ( <p>当前地区暂无可用订阅播放渠道</p> )} <p>租赁平台</p> {rentItems.length ? ( <ul className={classes.logolist}>{rentItems}</ul> ) : ( <p>当前地区暂无可用租赁渠道</p> )} </> ) } return ( <> <button onClick={receiveStreaming}>查看播放渠道</button> <div className={classes.streaminglogos}> {streamingOn && <div>{displayStreaming(streamingData)}</div> } </div> </> ) } export default Streaming
内容的提问来源于stack exchange,提问作者Jon Stephen
相关产品推荐
相关产品推荐

