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

React TS父组件状态变更致列表子组件重复挂载问题求解

React + TypeScript组件状态重置问题解决方案

问题场景

在React(TypeScript)项目中,父组件App包含列表组件ComponentList,后者渲染多个Number子组件。当点击Update按钮修改父组件的x状态(未改动列表数据)时,已修改过颜色的Number组件会重置为初始颜色,内部状态丢失。

代码复现

App.tsx

import { useState } from 'react'
import Number from './Number'
import './App.css'

function App() {
    const [list, setList] = useState(["1", "2", "3", "4"]);
    const [x, setX] = useState({});

    function ComponentList(){
        return(<>
            {list.map(i => <Number num={i} key={i}/>) }
        </>)
    }

    return (
        <div className="App">
            <ComponentList/>
            <div onClick={()=>{
                setX({});
            }}>Update</div>
        </div>
    )
}

export default App

Number.tsx

import React, {useState} from 'react'

type props = {
    num: string
}

function Number({num}:props) {
    const [color, setColor] = useState("rgb(0, 0, 0)");

    return (
        <div style={{color: color}} onClick={()=>{
            setColor("rgb(255, 255, 255)");
        }}>
            {num}
        </div>
    )
}

export default Number

问题根源

核心原因是ComponentList被定义为App组件内部的函数组件:每次App因状态变更(比如x更新)重新渲染时,ComponentList函数都会被重新创建。React在对比组件类型时,会将新创建的ComponentList视为完全不同的组件类型,因此会卸载旧的ComponentList及其所有子组件(Number),再挂载新的实例,导致Number的内部color状态被重置。

解决方法

以下两种方案均可解决问题,且保留列表组件的结构:

方案1:将ComponentList提取为独立组件

把ComponentList移到App组件外部,作为独立组件接收list作为props。这样ComponentList的引用不会随App渲染而变化,React会识别为同一组件类型,不会触发子组件的卸载/重新挂载:

import { useState } from 'react'
import Number from './Number'
import './App.css'

// 提取为独立组件
const ComponentList = ({ list }: { list: string[] }) => {
  return (
    <>
      {list.map(i => <Number num={i} key={i} />)}
    </>
  )
}

function App() {
    const [list, setList] = useState(["1", "2", "3", "4"]);
    const [x, setX] = useState({});

    return (
        <div className="App">
            <ComponentList list={list} />
            <div onClick={()=> setX({})}>Update</div>
        </div>
    )
}

export default App

方案2:将ComponentList改为普通渲染函数

如果需要保留在App内部定义,可将ComponentList改为普通函数,在App的return中直接调用(而非作为组件标签使用)。这样React会直接渲染函数返回的元素,不会将其视为独立组件,避免卸载/重新挂载:

import { useState } from 'react'
import Number from './Number'
import './App.css'

function App() {
    const [list, setList] = useState(["1", "2", "3", "4"]);
    const [x, setX] = useState({});

    // 改为普通渲染函数
    const renderComponentList = () => {
        return(<>
            {list.map(i => <Number num={i} key={i}/>) }
        </>)
    }

    return (
        <div className="App">
            {renderComponentList()} {/* 直接调用函数 */}
            <div onClick={()=> setX({})}>Update</div>
        </div>
    )
}

export default App

额外优化(可选)

为了避免Number组件在父组件渲染时不必要的重渲染,可使用React.memo包裹Number组件:

// Number.tsx
import React, { useState, memo } from 'react'

type props = {
    num: string
}

const Number = memo(({num}:props) => {
    const [color, setColor] = useState("rgb(0, 0, 0)");

    return (
        <div style={{color: color}} onClick={()=>{
            setColor("rgb(255, 255, 255)");
        }}>
            {num}
        </div>
    )
})

export default Number

内容的提问来源于stack exchange,提问作者Max W.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:52:44