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

如何修改多个React子组件state?解决open状态不可扩展问题

解决React侧边栏单选高亮问题及"Cannot add property open, object is not extensible"错误

问题根源

  • 错误原因:你试图直接修改props.children中的React元素属性,React元素是不可变(immutable)且不可扩展的对象,无法直接添加或修改其属性,因此抛出Cannot add property open, object is not extensible错误。
  • 状态管理问题:子组件SidebarOption和SideBarContainers各自维护open状态,父组件无法统一控制所有子组件的激活状态,无法实现“点击一项时其他项取消高亮”的单选需求。

解决方案:状态提升+单向数据流

将激活状态统一放在父组件SideBarItemList中管理,通过props将状态和回调传递给子组件,具体步骤如下:

1. 修改父组件SideBarItemList

维护activeId状态记录当前激活项的ID,通过React.Children.map和React.cloneElement给子组件传递激活状态和点击回调:

import { Component } from "react";
import React from "react";

class SideBarItemList extends Component{
    constructor(props){
        super(props);
        this.state ={
            activeId: null // 存储当前激活项的id
        };
    }

    // 处理子项点击,更新激活id
    handleItemClick = (id) => {
        this.setState({ activeId: id });
    }

    render(){
        // 遍历子组件,注入isActive和onClick props
        const renderedItems = React.Children.map(this.props.children, (child) => {
            if (child.props.id) {
                return React.cloneElement(child, {
                    isActive: this.state.activeId === child.props.id,
                    onClick: () => this.handleItemClick(child.props.id)
                });
            }
            return child;
        });

        return(
            <div>
                {renderedItems}
            </div>
        )
    }
}

export default SideBarItemList;

2. 修改子组件SidebarOption

移除内部open状态,通过props接收isActive和onClick:

import React, {Component} from "react";
import classes from "../../layout/Sidebar.module.css"
import { Link } from "react-router-dom";

class SidebarOption extends Component{
    render(){
        return (
        <div>
            <Link to={this.props.link || "#"}>
                <button
                    className={this.props.isActive ? classes.dropdownbtnopen : classes.dropdownbtn} 
                    onClick={this.props.onClick}
                >
                    {this.props.name}
                </button>
            </Link>
        </div>
        )
    }
}

export default SidebarOption;

3. 修改SideBarContainers组件(同理)

同样移除内部open状态,通过props接收激活状态和点击事件:

import React, {Component} from "react";
import classes from "../../layout/Sidebar.module.css"

class SideBarContainers extends Component{
    render(){
        return (
            <div>
                <button
                    className={this.props.isActive ? classes.dropdownbtnopen : classes.dropdownbtn}
                    onClick={this.props.onClick}
                >
                    {this.props.name}
                </button>
                {/* 原有options列表渲染逻辑保持不变 */}
                <div className={classes.optionsContainer}>
                    {this.props.options.map((opt, index) => (
                        <div key={index} className={this.props.notPressAble?.includes(index) ? classes.disabled : ""}>
                            {opt}
                        </div>
                    ))}
                </div>
            </div>
        )
    }
}

export default SideBarContainers;

关键说明

  • 状态提升:将共享的激活状态放在父组件,确保所有子组件的状态统一受控,符合React单向数据流原则。
  • React元素不可变性:使用React.cloneElement创建新的子组件实例并传递props,避免直接修改原有的不可变React元素,解决了object is not extensible错误。
  • 单选逻辑:通过activeId匹配子组件的id,确保同一时间只有一个项处于激活状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:07:10