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

如何为带展开折叠的Tree View实现单元格行内编辑

分类树节点行内编辑实现方案

核心思路

每个节点需要两种状态:文本显示态和编辑态,通过状态管理切换;编辑完成后递归更新树结构数据,同时保留展开/折叠功能。

具体实现步骤(结合React代码)

1. 给TreeItem组件添加编辑状态

在TreeItem组件内部维护编辑状态和临时输入值,点击节点文本触发编辑:

import { useState } from "react";

const TreeItem = ({ item, level, toggleExpand, isExpanded, updateItem }) => {
  // 编辑状态:是否处于编辑模式
  const [isEditing, setIsEditing] = useState(false);
  // 临时存储编辑内容,避免直接修改原始数据
  const [editValue, setEditValue] = useState(item.name);

  // 保存编辑内容
  const handleSave = () => {
    updateItem(item.id, editValue);
    setIsEditing(false);
  };

  // 取消编辑,恢复原内容
  const handleCancel = () => {
    setEditValue(item.name);
    setIsEditing(false);
  };

  return (
    <div style={{ paddingLeft: `${level * 20}px` }}>
      {/* 展开/折叠按钮,不受编辑状态影响 */}
      <button onClick={() => toggleExpand(item.id)}>
        {isExpanded ? "▼" : "▶"}
      </button>

      {/* 根据状态切换显示文本或输入框 */}
      {isEditing ? (
        <>
          <input
            type="text"
            value={editValue}
            onChange={(e) => setEditValue(e.target.value)}
            // 失焦自动保存
            onBlur={handleSave}
            // 回车保存、ESC取消
            onKeyDown={(e) => {
              if (e.key === "Enter") handleSave();
              if (e.key === "Escape") handleCancel();
            }}
            // 进入编辑时自动聚焦
            autoFocus
          />
          <button onClick={handleSave}>保存</button>
          <button onClick={handleCancel}>取消</button>
        </>
      ) : (
        // 点击文本进入编辑模式
        <span onClick={() => setIsEditing(true)}>{item.name}</span>
      )}

      {/* 递归渲染子节点 */}
      {isExpanded && item.children && item.children.map((child) => (
        <TreeItem
          key={child.id}
          item={child}
          level={level + 1}
          toggleExpand={toggleExpand}
          isExpanded={isExpanded}
          updateItem={updateItem}
        />
      ))}
    </div>
  );
};

2. 父组件实现递归更新树数据

因为树是嵌套结构,需要递归找到目标节点并更新名称,在父组件中添加updateItem函数:

const [treeData, setTreeData] = useState(/* 你的原始树数据 */);

// 递归更新节点名称
const updateItem = (targetId, newName) => {
  const updateRecursive = (nodes) => {
    return nodes.map(node => {
      if (node.id === targetId) {
        return {...node, name: newName};
      }
      // 递归更新子节点
      if (node.children) {
        return {...node, children: updateRecursive(node.children)};
      }
      return node;
    });
  };
  setTreeData(updateRecursive(treeData));
};

3. 传递更新函数给子组件

在渲染根TreeItem时,把updateItem传递下去:

<TreeItem
  item={treeData}
  level={0}
  toggleExpand={/* 你的展开/折叠函数 */}
  isExpanded={true}
  updateItem={updateItem}
/>

优化建议

  • 如果树节点数量较多,可以改用全局状态(比如useReducer)记录当前编辑的节点ID,代替每个节点单独维护isEditing,提升性能
  • 可以替换点击文本触发编辑的逻辑,改为添加单独的「编辑」图标按钮,避免误触
  • 自定义输入框样式,让编辑态和显示态的排版保持一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 11:07:05