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

React MUI Tree添加子节点后无法自动显示,需点击兄弟节点触发

React MUI Tree节点添加后不自动刷新的解决方案

问题根源

核心问题是直接修改了原始状态对象的引用,React无法检测到状态变化,导致Tree组件不会自动重渲染:

  • addChildHandler里直接操作全局的ROMItems对象,然后把同一个引用传给setV1Items,React认为状态未发生变化,因此不会更新组件。
  • 只有点击其他节点时,RomTree内部的selectedTreeNode状态变化触发重渲染,才会显示新添加的节点。

修复方案

1. 保证状态更新的不可变性

在App.js的addChildHandler中,必须基于当前的v1Items状态创建新的对象副本,避免直接修改原始对象。通过展开运算符实现浅拷贝(当前层级结构适用),确保状态引用发生变化。

2. 优化组件传参逻辑

将完整的v1Items对象传递给RomTree,而非拆分单个属性,让renderTree可以直接使用完整节点数据,简化代码。


修正后的代码

App.js

import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import RomTree from './components/RomTree'
import ChangeDriverForm from './components/ChangeDriverForm';

const ROMItems =  {
    nodeId: 'root',
    label: 'New Epic',
    type: 'Epic',
    children: [{
      nodeId:'child',
      label:'Child Epic',
      type: 'Epic',
      children: [{
        nodeId:'child2',
        label:'Child Epic',
        type: 'Epic',
        children: []}] 
    }]
};

const App = () => {
  const [v1Items, setV1Items] = useState(ROMItems);

  const addChildHandler = (enteredChild) => {
    // 创建新的根节点副本,避免修改原始状态
    const newRoot = {
      ...v1Items,
      children: [...v1Items.children, enteredChild]
    };
    setV1Items(newRoot);
  };
   
  return (
    <div className="App">
      <div>
        <ChangeDriverForm/>
      </div>
      <div>Rom Panel4</div>
      {/* 传递完整的树形数据对象 */}
      <RomTree onAddChild={addChildHandler} treeData={v1Items}></RomTree>
    </div>
  );
}

export default App;

RomTree.js

import React, { useState } from "react";
import { TreeItem, TreeView } from '@mui/lab'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import ChevronRightIcon from '@mui/icons-material/ChevronRight'

function RomTree(props){
    const [selectedTreeNode, setSelectedTreeNode] = useState('root');

    function treeNodeSelect(event, nodeIds){
        setSelectedTreeNode(nodeIds);   
    };

    function epicButtonHandler(){
        const newEpicNode = {
            nodeId: Math.random().toString(),
            label: 'Child Epic',
            type: 'Epic',
            children: []
        };
        props.onAddChild(newEpicNode);
    };

    function backlogButtonHandler(){
        const newBacklogNode = {
            nodeId: Math.random().toString(),
            label: 'Child Backlog',
            type: 'Backlog',
            children: []
        };
        props.onAddChild(newBacklogNode);
    };

    const renderTree = (nodes) => (
        <TreeItem
            key={nodes.nodeId} 
            nodeId={nodes.nodeId}
            label={nodes.label}>
            {Array.isArray(nodes.children)
                ? nodes.children.map((node) => renderTree(node))
                : null}
        </TreeItem>
    );

    return(
        <div>
            <TreeView
                defaultCollapseIcon={<ExpandMoreIcon />}
                defaultExpandIcon={<ChevronRightIcon />}
                onNodeSelect={treeNodeSelect}
                aria-label="controlled"
                alignment="left"
                sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}>
                {/* 使用传入的完整树形数据渲染 */}
                {renderTree(props.treeData)}
            </TreeView>
            <button onClick={epicButtonHandler}>Add Epic</button>    
            <button onClick={backlogButtonHandler}>Add Backlog</button>    
        </div>            
    );
}

export default RomTree;

额外说明

  • 现在点击添加按钮后,会创建全新的根节点对象,React检测到状态引用变化,会立即触发RomTree重渲染,新节点直接显示。
  • 如果需要给指定节点添加子节点(而非固定根节点),可以在addChildHandler中传入目标节点的nodeId,递归查找并更新对应节点的children,同样要保持状态的不可变性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:07:04