React调用fetch API后setState未即时更新问题排查
问题根源
你的代码有4个核心错误,导致状态更新不符合预期:
useEffect回调的参数写法完全错误。useEffect执行回调时不会传入任何状态参数,你写在形参里的CP、ListVille是和组件状态完全无关的未定义局部变量,实际请求的地址是http://localhost:3000//api/1.0/getCommunes/undefined,根本拿不到对应邮编的城镇数据。- 省略了
useEffect的依赖数组。不传依赖数组时,effect会在每一次组件渲染完成后全量执行:输入CP触发重渲染→执行effect发请求→请求返回调用setList触发重渲染→再次执行effect发请求,形成无限请求循环,状态会被重复覆盖,永远无法稳定。你想要CP变化时触发请求,只需要把CP加入依赖数组即可,不需要完全省略依赖数组。 - 调用
setList后立刻打印ListVille拿不到新值。React的状态更新是异步批处理机制,当前执行上下文中的ListVille还是本轮渲染的旧值,必须等组件完成下一次重渲染后,才能获取到更新后的状态值。 ListVille没有设置合法初始值。你给ListVille的初始值是undefined,如果渲染时直接访问ListVille[0]这类属性,会直接抛出Cannot read properties of undefined的运行时错误。
修正后代码
import React, {useEffect, useState} from 'react'; import {Navigate} from 'react-router-dom' import Entete from './MEP/entete' function Localisation() { const [CP, setCP]= React.useState(''); const [city, setCity]= React.useState(''); // 给ListVille设置空数组初始值,避免访问属性时报错 const [ListVille, setList]= React.useState([]); const [goToExploit, setGoToExploit] = useState(false) useEffect ( () => { // CP为空时不发送无效请求 if (!CP.trim()) return // 修正接口地址的双斜杠问题,直接从组件闭包读取最新的CP值 fetch(`http://localhost:3000/api/1.0/getCommunes/${CP}`) .then( (response) => { console.log('请求响应对象:', response) return response.json() }) .then(resData =>{ setList(resData) console.log('接口返回的城镇数据:', resData) }) // 依赖数组仅传入CP,只有CP变化时才会重新执行请求逻辑 }, [CP]) // 如需监听ListVille的更新,单独写effect监听即可 useEffect(() => { console.log('当前ListVille最新状态:', ListVille) }, [ListVille]) function handleSubmit(event) { event.preventDefault() setGoToExploit(true) } if(goToExploit) { return <Navigate push to={`/exploitation`} /> } function handleChange(event) { const inputCP = event.target.value.trim() setCP(inputCP) } return ( <div> <Entete titre="Localisation"/> <form onSubmit={handleSubmit}> <div className='titre'> Saisissez votre code postal {CP}</div> <input className='input' value={CP} onChange={handleChange} placeholder='Code Postal' type='text' required/> {/* 加可选链操作符兼容数据未返回的场景,避免报错 */} <div className='paragraphe'> Test {CP==='41160' ? ListVille[0]?.key ?? '暂无匹配城镇' : '请输入邮编'}</div> <div className='centrer'> <button type='submit' className='validation'> Valider </button> </div> </form> </div> ); } export default Localisation;
修正后逻辑:输入邮编时CP更新,触发effect发请求拉取对应城镇数据,数据返回后更新ListVille,组件自动重渲染展示最新数据,不会出现无限循环,也不会有状态不更新的问题。
内容的提问来源于stack exchange,提问作者Aliénor
相关产品推荐
相关产品推荐

