You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useEffect生成的组件列表在JSX中无法显示求助

React组件列表不显示问题排查与修复

问题描述

我是一名编程爱好者,正在学习基础React与API(fetch、then块等基础内容)。我通过useEffect遍历数组发起多个API请求,将返回的数据以对象数组形式存入React状态gecko中。之后我需要遍历该对象数组,创建<Coin>组件列表并传入state(gecko)作为props.data,同时设置key。在useEffect内打印时,coinList变量似乎已正确存储<Coin>组件列表,但在JSX中使用{coinList}却无法显示,且<Coin>组件内打印props.data也无输出。

相关代码:

function App() {
  const [gecko, setGecko] = React.useState([
      {id:'bitcoin',image:{small:'https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579'},market_data:{ market_cap_change_24h_in_currency:{usd:'Not Available' }}},
      {id:'dogecoin',image:{small:'https://assets.coingecko.com/coins/images/5/small/dogecoin.png?1547792256'},market_data:{market_cap_change_24h_in_currency:{usd:'Not Available'}}},
      {id:'ethereum',image:{small:'https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880'},market_data:{market_cap_change_24h_in_currency:{usd:'Not Available'}}},
      {id:'litecoin',image:{small:'https://assets.coingecko.com/coins/images/2/small/litecoin.png?1547033580'},market_data:{market_cap_change_24h_in_currency:{usd:'Not Available'}}}
    ])

  const coinGeckoUrl = `https://api.coingecko.com/api/v3/coins/`
  const coinArr = ['bitcoin','dogecoin','ethereum','litecoin']
  let coinList = [];

React.useEffect(()=>{
      Promise.all(coinArr.map(coin => fetch(`${coinGeckoUrl}${coin}`)))
        .then(results => {
          Promise.all(results.map((res)=>{
            return res.json()
          }))
          .then(data=>{
            console.log(`gecko data block`)
           setPreviousState('gecko', data)
            coinList = gecko.map((item)=>{
              return <Coin key={nanoid()} data={item} />
            })
            console.log(coinList)
          })
          .catch(err=>console.log(err))
        })
  },[])
  
  function saveToLocalStorage(name, info){
    console.log(`calling save to localStorage`)
    localStorage.setItem(name, JSON.stringify(info))
  }

  function retrieveLocalStorage(name){
    console.log('retrieving local Storage')
    switch(name) {
      case 'unsplash':
        unsplashController = JSON.parse(localStorage.getItem(name))
        break;
      case 'gecko':
        geckoController = JSON.parse(localStorage.getItem(name))
        break;
      default:
        console.log('bad switch statement')
    }
    
  }

  function setPreviousState(state, fetchData){
    console.log(`calling setPrevState ${state}`)
    let hadData;
    if(fetchData === undefined && retrieveLocalStorage(state) !== undefined){
      let prevData = retrieveLocalStorage(state);
      switch(state) {
        case 'unsplash':
            hadData = true;
            setUnsplash(()=>{
              return {...prevData}
            })
          break;
        case 'gecko':
            hadData = true;
            setGecko(()=>{
              return {...prevData}
            })
          break;
        default:
          hadData = false;
          console.log('bad switch statement')
      }
    } else if(fetchData !== undefined){
      console.log(`we had fetchData`)
      switch(state) {
        case 'unsplash':
            hadData = true;
            saveToLocalStorage(state, fetchData)
            setUnsplash(()=>{
              return {...fetchData}
            })
          break;
        case 'gecko':
            hadData = true;
            setGecko(()=>{
              return [...fetchData]
            })
          break;
        default:
          hadData = false;
          console.log('bad switch statement')
      }
    } else {
      hadData = false;
    }
    return hadData;
  }

return (
    <div id="--app-app-container">
      <div id='--app-dashboard-container' className='container' style={styles.container} >
        <div id='--app-top-container'>
          <div className='--app-left-container'>
            <h1>Left Container</h1>
            {coinList}
          </div>
          <div className='--app-right-container'>
            <h1>Right Container</h1>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
          </div>
        </div>        
      </div>
    </div>
  )
}

问题原因与修复步骤

1. 普通变量coinList无法触发组件重渲染

coinList是组件内的普通let变量,React不会追踪它的变化。API请求完成后给它赋值,组件不会重新渲染,JSX里的{coinList}始终是初始的空数组。

修复: 直接在JSX中遍历gecko状态生成组件,无需额外变量。因为gecko是React状态,更新后会自动触发组件重渲染,JSX会同步更新。

修改return部分的代码:

<div className='--app-left-container'>
  <h1>Left Container</h1>
  {gecko.map(item => (
    <Coin key={item.id} data={item} />
  ))}
</div>

2. 异步状态更新后立即访问旧值

React状态更新是异步的,调用setPreviousState后马上用gecko.map,此时gecko还是更新前的旧值,生成的组件用的不是最新的API数据。

修复: 移除useEffect里生成coinList的代码,依赖gecko状态的组件列表直接在JSX中渲染,确保每次gecko更新都用最新数据。

3. setPreviousState中数组转对象的错误

在从localStorage读取gecko数据时,代码用了return {...prevData},把数组展开成对象(索引转为键),导致gecko变成对象而非数组,后续遍历失效。

修复: 读取localStorage数据时返回数组:

case 'gecko':
  hadData = true;
  setGecko(()=>{
    return [...prevData] // 展开数组,保持数组类型
  })
break;

4. key值使用不当

用nanoid()生成key会导致每次渲染都生成新key,React会重新创建所有<Coin>组件,既影响性能也可能导致状态丢失。

修复: 使用每个币的唯一id作为key(比如item.id),这是React推荐的稳定key用法。

简化后的useEffect代码

可以去掉coinList变量,useEffect只负责更新状态:

React.useEffect(()=>{
  Promise.all(coinArr.map(coin => fetch(`${coinGeckoUrl}${coin}`)))
    .then(results => Promise.all(results.map(res => res.json())))
    .then(data => {
      setPreviousState('gecko', data)
    })
    .catch(err => console.log(err))
},[])

内容的提问来源于stack exchange,提问作者Rawle Juglal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:45:38