React componentDidMount内第二次调用setState不生效问题
问题根因
问题本质是两个点共同导致的:
- React类组件
setState不返回Promise,你的await没有等待效果:你在第一次setState保存chars前加了await,但setState本身是异步执行且不返回Promise的,await会直接拿到undefined立刻往下执行,此时this.state.chars还是初始值空数组[]。 - 随机取值时数据源为空:执行
displayChars时从空数组里按索引取元素,拿到的自然全是undefined。你误以为console能打印出正常数据,是浏览器控制台的懒解析特性导致的:控制台打印引用类型(数组/对象)时不会立刻序列化内容,等你手动展开日志时,内存里的数组已经被后续操作修改了,你看到的不是代码执行那一刻的真实值。
另外代码里还有两个潜在bug:
- 随机索引硬编码乘24,过滤后带有效图片的角色总数不一定是24,极易出现索引越界拿到
undefined trimData里用map做遍历过滤但没有使用返回值,属于无意义的API使用,用filter更合适
修复方法
最稳妥的方案是绕开setState的异步时序问题:拿到接口数据、过滤完有效列表后,直接基于过滤后的列表生成待展示的随机项,最后一次性更新所有state,完全不需要在中间步骤读取this.state。
完整修正后的相关代码如下:
async componentDidMount() { try { const rawCharacters = await this.loadData(); // 过滤出带有效图片的角色 const validChars = this.trimData(rawCharacters); // 直接传入有效角色列表生成随机展示项,不依赖state里的chars const displayList = this.generateDisplayList(validChars); // 一次性更新所有state this.setState({ chars: validChars, displayedChars: displayList }, () => { // setState的回调函数是state更新完成的唯一可靠时机,这里打印才能拿到最新值 console.log('初始化完成的state:', this.state) }) } catch (err) { console.error('数据加载失败:', err) } } trimData(characters) { // 直接用filter过滤,不需要手动遍历push return characters.filter(char => char.image !== "") } // 改名更符合语义,这个方法不需要包Promise,本身是同步逻辑 generateDisplayList(validChars) { const list = [] const totalValid = validChars.length for(let i = 0; i < this.state.numImg; i++) { // 随机索引用有效数据的实际长度做上限,不要硬编码24 const randomIndex = Math.floor(Math.random() * totalValid) list.push(validChars[randomIndex]) } return list }
如果你一定要分两次调用setState,必须把第二次displayChars的逻辑放到第一次setState的第二个回调参数里执行,只有这个回调能保证第一次的state已经更新完成,await是做不到的。
内容的提问来源于stack exchange,提问作者aLpZ
相关产品推荐
相关产品推荐

