React子组件中fetch数据为何显示undefined?
问题分析与解决方案
核心问题
问题根源在于父组件将JSX元素直接存入了state。点击按钮调用setDiv时,传递给Race组件的fetchData是当时的状态快照,后续父组件fetchData更新后,存在state里的JSX不会自动重新渲染,导致子组件始终拿到初始状态的fetchData。另外,父、子组件的useEffect都缺少依赖数组,会引发无限重复执行fetch请求的潜在问题。
解决方案步骤
1. 移除JSX存state的写法,改用条件渲染
不要把JSX存在state里,而是在组件渲染逻辑中直接根据状态判断要显示的内容。这样每次父组件状态更新(比如fetchData变化),都会生成最新的JSX,传递最新的props给子组件。
修改后的父组件代码:
export default function CharCreator () { const [fetchData, setFetchData] = useState(...someObj) // 用状态标记当前视图类型,而非存储JSX const [currentView, setCurrentView] = useState('initial') // 修复useEffect,添加空依赖数组,仅组件挂载时执行一次fetch useEffect(() => { const fetchCall = async () => { try{ const res = await fetch('https://www.dnd5eapi.co/api/races') const data = await res.json() setFetchData(prevData => ({ ...prevData, race_list:data.results })) }catch(err){ console.error('Error fetching data: ', err) } } fetchCall() }, []) const handleNewChar = (e, newView) => { e.preventDefault() setCurrentView(newView) } // 根据currentView渲染对应内容 const renderCurrentView = () => { if(currentView === 'class/race'){ return ( <div id='race_class'> <div id='race'> <Race functions={{ setFetchData:setFetchData, raceFetch: raceFetch, fetchData:fetchData }} /> </div> </div> ) } // 默认显示初始视图 return ( <div> <button onClick={(e) => {handleNewChar(e, 'class/race')}}>Create Character</button> </div> ) } return( <div id='outerParent'> <form> {renderCurrentView()} <p> <button onClick={logs}>fetchData</button> </p> </form> </div> ) }
2. 移除子组件中重复的fetch请求
父组件已负责获取race_list数据,子组件直接使用父组件传递的fetchData即可,同时优化子组件渲染逻辑:
修改后的子组件代码:
export default function Race({functions}) { const {fetchData, raceFetch, setFetchData} = functions const [spawn, setSpawn] = useState(0) // 补充缺失的spawn状态 const isHidden = fetchData?.race?.name ? '' : 'none' // 移除重复的fetch请求 // 直接在render中渲染select,无需存入state const renderRaceSelect = () => { if(!fetchData?.race_list) return null return ( <p> <select onChange={verifyInput} id='raceSet' defaultValue=''> <option value='' disabled >Select a Race</option> {fetchData.race_list.map((race, i) => ( <option key={i} data-url={race.url} value={race.name}> {race.name} </option> ))} </select> </p> ) } const verifyInput = (e) => { const input = e.target.value const selected = e.target.options[e.target.selectedIndex] const url = selected.getAttribute('data-url') raceFetch(url) setSpawn(prevSpawn => prevSpawn + 1) } return( <div> <div> <h2>Race</h2> </div> {renderRaceSelect()} <div> {spawn > 0 ? <div id='raceInfo'> <RaceInfo functions={{ fetchData:fetchData, setFetchData:setFetchData }} /> </div> :''} </div> </div> ) }
关键说明
- 不要把JSX存在state中:React的state应存储数据而非UI元素,直接在渲染逻辑中生成JSX,才能保证状态更新时渲染最新内容。
- useEffect必须加依赖数组:空依赖数组确保fetch仅在组件挂载时执行一次,避免无限循环请求。
- 子组件依赖父组件props:父组件状态更新后,props会自动传递给子组件,子组件重新渲染即可拿到最新的
fetchData。
内容的提问来源于stack exchange,提问作者Christopher Dirksen
相关产品推荐
相关产品推荐

