React中Promise阻塞致状态未更新返回null的问题求助
解决方案
你的问题核心是React状态更新的异步特性导致的——setDetails和setString都是异步操作,当组件因details更新触发重渲染时,string状态可能还未完成更新,此时访问data.currencies[string]就会因string为null而抛出错误。
优化方案:移除冗余的string状态
string是从接口返回的data.currencies派生出来的临时值,不需要单独存入状态。直接在渲染阶段根据当前循环的data计算即可,完全避免异步状态更新带来的不一致问题,同时简化代码逻辑:
import React, { useState, useEffect } from 'react' import { useParams } from 'react-router-dom' import Header from '../components/header.js' import { BsArrowLeft } from 'react-icons/bs' const Country = () => { const { countryName } = useParams(); const [country, setCountry] = useState(countryName); const [details, setDetails] = useState(null); // 移除冗余的string状态 useEffect(() => { async function fetchData() { const res = await fetch(`https://restcountries.com/v3.1/name/${country}`) const data = await res.json() setDetails(data) // 移除setString操作 } fetchData() }, [country]) return ( <main className='h-screen flex flex-col items-center w-full overflow-x-hidden bg-verylightgrey dark:bg-verydarkblue overflow-scroll'> <div className='flex flex-col items-center w-full'> <Header/> <section className='flex justify-items-start w-full'> <button className='border-2 ml-7 mt-10 mb-16 rounded shadow-md hover:shadow-inner cursor-pointer w-3/12 flex flex-row items-center justify-center text-sm font-light'><span className='mr-2 text-lg'><BsArrowLeft/></span>Back</button> </section> {details && details.map((data, index) => <div className='w-full flex flex-col items-center' key={index}> <img className='rounded mb-11 w-10/12 h-6/6' alt={data.name.common} src={data.flags.png}/> <section className='flex flex-col w-full '> <h1 className='font-extrabold text-xl ml-7'>{data.name.common}</h1> <p className='ml-7 mt-4 text-sm font-light'><span className='text-sm font-semibold'>Official Name: </span>{data.name.official}</p> <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Population: </span>{data.population.toLocaleString()}</p> <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Region: </span>{data.region}</p> <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Sub Region: </span>{data.subregion}</p> <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Capital: </span>{data.capital?.join(', ')}</p> <p className='ml-7 mt-8 text-sm font-light'><span className='text-sm font-semibold'>Top Level Domain: </span>{data.tld.join(', ')}</p> {/* 直接从当前data中提取货币名称,支持多货币情况 */} <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Currencies: </span> {Object.values(data.currencies).map(curr => curr.name).join(', ')} </p> {/* 处理语言数组 */} <p className='ml-7 mt-2 text-sm font-light'><span className='text-sm font-semibold'>Languages: </span> {Object.values(data.languages).join(', ')} </p> <h2 className='font-semibold text-base ml-7 mt-8'>Border Countries:</h2> {/* 遍历边境国家数组,渲染多个按钮 */} <div className='flex flex-wrap ml-7 mt-4 gap-2'> {data.borders?.map(border => ( <button key={border} className='border-2 shadow-md rounded cursor-pointer w-24 h-6 text-xs font-light'>{border}</button> ))} </div> </section> </div>)} </div> </main> ); } export default Country;
额外优化点说明
- 对
population使用toLocaleString()格式化数字,提升可读性 - 对
capital、borders等可能为数组的字段,使用可选链?.避免空值报错,并通过join(', ')或map渲染多个值 - 直接提取
data.currencies和data.languages的value值,无需依赖额外状态,逻辑更直接
内容的提问来源于stack exchange,提问作者Curtis Sanderson
相关产品推荐
相关产品推荐

