React useEffect处理API响应时生命周期异常导致countriesData.map报错
核心问题根因
- 状态流转逻辑缺失
- 每次
searchValue变化触发新请求时,没有重置error和loading状态,旧状态会被复用,新请求返回前页面会用旧状态渲染,一旦出现状态异常就会直接走到map逻辑。 - 请求失败的
!res.ok分支仅设置了error,没有同步把loading设为false,若出现异常状态流转就会跳过错误判断。 - 整个异步请求没有用
try/catch包裹,网络错误、JSON解析错误等异常会直接中断函数执行,既不会触发setError也不会更新loading,导致页面保留error=false、loading=false的旧状态。
!res.ok不触发的问题
你测试的搜索词大概率都能命中接口返回200状态码,输入完全不存在的国家名比如asdfghjkl123时,接口会返回404,此时res.ok会为false,就能进入对应分支。
修复后完整代码
import { useEffect, useState } from 'react' import CountryCard from '../CountryCard' import { countries } from './CountriesList.module.css' const CountriesList = ({ searchValue }) => { const [error, setError] = useState(false) const [loading, setLoading] = useState(true) const [countriesData, setCountriesData] = useState([]) useEffect(() => { // 新增请求取消逻辑,避免快速输入时的竞态问题 const abortController = new AbortController() const signal = abortController.signal const getCountries = async () => { // 每次请求前重置状态 setError(false) setLoading(true) try { let res = await fetch('https://restcountries.com/v2/all', { signal }) const trimedValue = searchValue.trim() if (trimedValue !== '') { // 搜索参数编码,避免特殊字符导致请求错误 res = await fetch( `https://restcountries.com/v2/name/${encodeURIComponent(trimedValue)}`, { signal } ) } if (!res.ok) { throw new Error('请求失败') } const data = await res.json() // 校验返回数据格式,避免非法值赋值 if (Array.isArray(data)) { setCountriesData(data) } else { throw new Error('数据格式错误') } } catch (err) { // 忽略主动取消的请求错误 if (err.name !== 'AbortError') { setError(true) } } finally { // 无论成功失败都更新loading状态 if (!signal.aborted) { setLoading(false) } } } getCountries() // 依赖变化或组件卸载时取消未完成的请求 return () => abortController.abort() }, [searchValue]) if (error) { return <div>Error: please check the country you have typed</div> } if (loading) { return <div>Loading...</div> } return ( <section className={`${countries} columns is-multiline`}> {countriesData.map((country) => ( <CountryCard {...country} key={country.name} /> ))} </section> ) } export default CountriesList
额外优化说明
- 移除了内部不必要的
Content组件,直接在主组件内写渲染逻辑即可,避免不必要的性能开销 - 对搜索参数做了
trim和编码处理,避免空内容、特殊字符导致的请求异常 - 新增了请求取消逻辑,避免快速输入时多个请求先后返回导致的数据覆盖问题
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

