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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 02:18:02