如何修改多个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
相关产品推荐
相关产品推荐

