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

如何在rc-tree中实现勾选节点时联动修改父子节点checked属性

解决rc-tree勾选节点同步修改父子节点checked属性的问题

我来帮你搞定这个rc-tree的勾选同步问题,分两步走:先解决你遇到的ref获取节点错误的问题,再实现勾选时同步父子节点checked属性的逻辑。


一、先修正Ref获取节点的问题

你现在遇到onCheck总是拿到最后一个节点的问题,是因为把所有TreeNode的ref都赋值给了同一个this.myRef,循环里后面的节点会直接覆盖前面的。正确的做法是用一个对象来存储每个节点的ref,以节点的key作为唯一标识:

class YourTreeComponent extends React.Component {
  constructor(props) {
    super(props);
    this.nodeRefs = {}; // 用对象存储每个节点的ref,key对应节点的key
    this.state = {
      treeData: [...yourInitialTreeData], // 初始化树数据
      checkedKeys: [],
      expandedKeys: [],
      autoExpandParent: true
    };
  }

  render() {
    const loop = (data) => {
      return data.map((item) => {
        const treeNodeProps = {
          ref: node => this.nodeRefs[item.key] = node, // 将每个节点的ref存入nodeRefs
          title: item.name,
          key: item.key,
          checked: item.checked
        };

        if (item.children && item.children.length) {
          return (
            <TreeNode {...treeNodeProps}>
              {loop(item.children)}
            </TreeNode>
          );
        }
        return <TreeNode {...treeNodeProps} isLeaf={item.isLeaf} />;
      });
    };

    return (
      <div className="draggable-container">
        <Tree
          expandedKeys={this.state.expandedKeys}
          onExpand={this.onExpand}
          autoExpandParent={this.state.autoExpandParent}
          draggable
          onDragStart={this.onDragStart}
          onDragEnter={this.onDragEnter}
          onDrop={this.onDrop}
          onSelect={this.onSelect}
          checkable
          onCheck={this.onCheck}
          checkedKeys={this.state.checkedKeys}
        >
          {loop(this.state.treeData)}
        </Tree>
      </div>
    );
  }
}

不过其实更推荐直接通过onCheck事件的参数获取当前节点——rc-tree的onCheck第二个参数event里已经包含了当前操作的节点对象event.node,不需要依赖ref也能拿到目标节点。


二、实现勾选同步父子节点checked的逻辑

你的需求是勾选任意节点时,同步修改该节点、所有父节点、所有子节点的checked属性。我们可以通过操作树数据的状态来实现,因为React是数据驱动视图的,修改数据后重新渲染即可。

1. 编写递归工具函数

首先需要几个递归函数来处理节点的修改:

// 递归设置当前节点及所有子节点的checked状态
const setChildrenChecked = (node, checked) => {
  node.checked = checked;
  if (node.children?.length) {
    node.children.forEach(child => setChildrenChecked(child, checked));
  }
};

// 向上遍历找到所有父节点并设置checked状态
const setParentChecked = (treeData, targetKey, checked) => {
  // 递归查找目标节点的所有父节点路径
  const findParentPath = (data, key, parentNodes = []) => {
    for (const node of data) {
      if (node.key === key) return parentNodes;
      if (node.children?.length) {
        const result = findParentPath(node.children, key, [...parentNodes, node]);
        if (result) return result;
      }
    }
    return null;
  };

  const parentNodes = findParentPath(treeData, targetKey);
  if (parentNodes) {
    parentNodes.forEach(node => node.checked = checked);
  }
};

// 收集所有checked为true的节点key,用于更新checkedKeys状态
const collectCheckedKeys = (data) => {
  let keys = [];
  data.forEach(node => {
    if (node.checked) keys.push(node.key);
    if (node.children?.length) keys = keys.concat(collectCheckedKeys(node.children));
  });
  return keys;
};

2. 完善onCheck方法

在onCheck方法里调用上面的工具函数,修改树数据并更新状态:

onCheck = (checkedKeys, event) => {
  const currentNode = event.node;
  const currentKey = currentNode.key;
  const isChecked = checkedKeys.includes(currentKey); // 判断是勾选还是取消勾选

  // 深拷贝树数据,避免直接修改原state
  let newTreeData = JSON.parse(JSON.stringify(this.state.treeData));

  // 找到当前节点在新数据中的引用
  const findNode = (data, key) => {
    for (const node of data) {
      if (node.key === key) return node;
      if (node.children?.length) {
        const result = findNode(node.children, key);
        if (result) return result;
      }
    }
    return null;
  };

  const targetNode = findNode(newTreeData, currentKey);
  if (targetNode) {
    // 设置当前节点及所有子节点的checked
    setChildrenChecked(targetNode, isChecked);
    // 设置所有父节点的checked
    setParentChecked(newTreeData, currentKey, isChecked);
  }

  // 收集所有已勾选的节点key
  const newCheckedKeys = collectCheckedKeys(newTreeData);

  // 更新状态,触发树的重新渲染
  this.setState({
    treeData: newTreeData,
    checkedKeys: newCheckedKeys
  });
};

三、补充说明

  • 这里用JSON.parse(JSON.stringify())深拷贝树数据,适合数据结构简单的场景;如果你的节点数据包含函数、Symbol等特殊类型,建议使用lodash.cloneDeep来深拷贝。
  • rc-tree本身支持半选(indeterminate)状态,但你的需求是强制所有关联节点全选/取消,所以上面的逻辑会覆盖半选状态,直接设置所有节点的checked为统一值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:40:24