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

ReactJS问题:调用useState setter后父组件未触发重新渲染

问题描述

父组件FatherComponent通过useState维护一个对象列表objectList,遍历该列表生成子组件ChildComponent。点击父组件的「Add Component」按钮可以添加新子组件,但点击子组件的「Remove Component」按钮时,虽然objectList确实被修改了,却不会触发父组件重新渲染,只有添加按钮能触发重渲染。

相关代码如下:

父组件代码

const FatherComponent = ({data}) => {

    const [objectList, setObjectList] = useState(data.objects)

    // 每个objectList元素创建时都会分配递增的唯一id,用于删除对应对象
    const removeComponent = (id) => {
        let newObjectList = objectList

        let index = newObjectList.findIndex(object => object.id === id);

        newObjectList.splice(index, 1);

        setObjectList(newObjectList)
    }

    const addComponent = (prevObjects) => {

        setObjectList([...prevObjects, new Object(data.newObjectId)])
        data.newObjectId += 1
    }

    useEffect(() => {
        data.objects = objectList
    }, [objectList, data])

    return (
        <Fragment>

            ...

            <button onClick={() => addComponent(objectList)}>Add Component</button>
                {objectList.map((childComponent) => (
                    <ChildComponent
                        object={childComponent}
                        removeComponent={() => removeComponent(childComponent.id)} />
                ))}
        </Fragment>

    )
}

子组件代码

const ChildComponent = ({ object, removeComponent }) => {

    return (

        ...
    
        <button onClick={removeComponent}>Remove Component</button>

        ...

    );
}

注:希望objectList作为大型类结构的一部分存储工具信息,因此在useEffect中同步更新data.objects;另外误以为每次重渲染时useState都会用data的值初始化,所以必须同步更新。

疑问:调用removeComponent时明明调用了useState的setter,为什么没触发父组件重渲染?

问题原因与解决方法

核心原因

React的状态更新依赖浅比较判断是否重渲染:当调用setObjectList时,如果传入的是和原状态同一引用的值,React会认为状态没有变化,从而跳过重渲染。

在removeComponent函数中:

let newObjectList = objectList // 仅复制原数组引用,未创建新数组
newObjectList.splice(index, 1); // 直接修改原数组(引用指向同一内存地址)
setObjectList(newObjectList) // 传入的仍是原数组引用,React判定状态未变更

而addComponent中用扩展运算符[...prevObjects, ...]创建了新数组,引用地址不同,React能识别到状态变化并触发重渲染。

此外还有两个潜在问题:

  1. 直接修改props.data(如data.objects = objectList和data.newObjectId += 1):React中props是只读的,直接修改会导致状态管理混乱,违反单向数据流原则。
  2. 误解useState初始化逻辑:useState(data.objects)仅在组件第一次挂载时使用初始值,后续重渲染不会再用data.objects覆盖状态,无需在useEffect中同步data.objects。

修复方案

1. 修正removeComponent,创建新数组更新状态

推荐两种实现方式:

方式一:用filter创建新数组(简洁高效)

const removeComponent = (id) => {
    // 过滤掉目标id的对象,返回新数组
    const newObjectList = objectList.filter(object => object.id !== id);
    setObjectList(newObjectList);
}

方式二:复制原数组后再修改

const removeComponent = (id) => {
    // 用扩展运算符复制原数组,创建新数组
    const newObjectList = [...objectList];
    const index = newObjectList.findIndex(object => object.id === id);
    if (index !== -1) { // 避免找不到id时出错
        newObjectList.splice(index, 1);
        setObjectList(newObjectList);
    }
}

2. 移除对props.data的直接修改

将newObjectId移到组件内部状态管理,避免修改props:

const FatherComponent = ({data}) => {
    // 用data.newObjectId作为初始值,维护组件内部的id计数器
    const [nextObjectId, setNextObjectId] = useState(data.newObjectId);
    const [objectList, setObjectList] = useState(data.objects)

    const addComponent = () => {
        // 使用内部状态的id创建新对象,之后更新计数器
        setObjectList(prev => [...prev, { id: nextObjectId }]);
        setNextObjectId(prev => prev + 1);
    }

    // 移除修改data的useEffect
    // useEffect(() => {
    //     data.objects = objectList
    // }, [objectList, data])

    return (
        <Fragment>
            <button onClick={addComponent}>Add Component</button>
            {objectList.map((childComponent) => (
                <ChildComponent
                    key={childComponent.id} // 给列表元素加唯一key,避免渲染异常
                    object={childComponent}
                    removeComponent={() => removeComponent(childComponent.id)} />
            ))}
        </Fragment>
    )
}

3. 给列表子组件添加key

遍历objectList生成ChildComponent时,必须添加唯一的key属性(用childComponent.id即可),这是React列表渲染的最佳实践,能避免不必要的重渲染和组件状态混乱。

最终效果

修改后,点击子组件的「Remove Component」按钮时,会创建新数组并更新状态,React识别到状态引用变化,会触发父组件重渲染,对应的子组件也会被正确移除。

内容的提问来源于stack exchange,提问作者Rodolfo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:35:02