React.StrictMode导致组件两次渲染,state.items未定义触发map报错如何解决?
1. 问题触发原因
核心报错和两次渲染没有直接因果关系,问题根因有两个:
- 你的
UserPlaylist组件没有初始化state,也没有在渲染时做空值保护。componentDidMount生命周期在首次渲染完成后才会执行,首次渲染时this.state本身就是未定义状态,调用this.state.items.map必然抛出错误。 - 你将父组件传入的
tracks冗余拷贝到组件内部state,但没有做props和state的同步逻辑,后续如果父组件传入的tracks或sortMode更新,子组件渲染内容也不会同步更新。
至于你观察到的两次渲染,确实是React开发环境下StrictMode的正常行为:它会故意重复触发组件挂载、渲染流程,用来检测代码中的副作用问题,生产环境不会复现。
2. 正确修复方案
最简单的方案是删掉冗余的state存储,直接使用props传入的数据,同时做空值保护:
import React, { Component } from 'react' import Track from '../components/track' export default class UserPlaylist extends Component { render() { // 给tracks加默认空数组,避免undefined调用map报错 const { sortMode, tracks = [] } = this.props // 拷贝数组后再排序,避免直接修改原props数据 const renderList = sortMode === 'new' ? [...tracks].reverse() : [...tracks] return ( <> {renderList.map(track => <Track key={track.id} trackObj={track}/>)} </> ) } }
如果确实需要把数据存在组件内部state,要先初始化state,同时加componentDidUpdate同步props变更:
import React, { Component } from 'react' import Track from '../components/track' export default class UserPlaylist extends Component { // 初始化state state = { sortMode: this.props.sortMode, tracks: this.props.tracks || [] } // 同步props变更 componentDidUpdate(prevProps) { if (prevProps.tracks !== this.props.tracks || prevProps.sortMode !== this.props.sortMode) { this.setState({ sortMode: this.props.sortMode, tracks: this.props.tracks }) } } render() { const { sortMode, tracks = [] } = this.state const renderList = sortMode === 'new' ? [...tracks].reverse() : [...tracks] return ( <> {renderList.map(track => <Track key={track.id} trackObj={track}/>)} </> ) } }
同时建议父组件给this.state.items设置初始值为空数组[],避免传入undefined。
3. 可排序组件的更优写法
推荐用函数组件+useMemo的实现,性能更好、逻辑更清晰,也方便后续扩展排序模式:
import React, { useMemo } from 'react' import Track from '../components/track' const UserPlaylist = ({ sortMode, tracks = [] }) => { // 用useMemo缓存排序结果,只有sortMode或tracks变更时才会重新排序,减少不必要的计算 const sortedTracks = useMemo(() => { const list = [...tracks] switch(sortMode) { case 'new': return list.reverse() // 后续新增排序模式直接加case即可,维护成本低 case 'hot': return list.sort((a, b) => b.playCount - a.playCount) case 'name': return list.sort((a, b) => a.name.localeCompare(b.name)) default: return list } }, [sortMode, tracks]) return ( <> {sortedTracks.map(track => <Track key={track.id} trackObj={track} />)} </> ) } export default UserPlaylist
如果数据是异步加载的,可以补充空状态/加载状态展示,优化用户体验。
内容的提问来源于stack exchange,提问作者Libra
相关产品推荐
相关产品推荐

