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

如何在React应用的react-flow中实现父节点点击展开/折叠子节点功能?

实现React Flow父节点展开/折叠所有子节点及关联边的简洁方案

核心思路

通过跟踪父节点的折叠状态,递归获取其所有后代节点,动态设置节点和边的hidden属性来实现展开/折叠功能,无需依赖节点ID的命名规则,完全基于节点间的连接关系控制显示。

完整实现代码

import React, { useState, useEffect } from 'react';
import ReactFlow, { Node, Edge, useNodesState, useEdgesState, getOutgoers, ReactMouseEvent } from 'reactflow';
import 'reactflow/dist/style.css';

// 初始节点和边示例
const initialNodes: Node[] = [
  { id: '1', position: { x: 100, y: 100 }, data: { label: '父节点1' }, type: 'custom' },
  { id: '2', position: { x: 100, y: 200 }, data: { label: '子节点1-1' } },
  { id: '3', position: { x: 100, y: 300 }, data: { label: '子节点1-2' } },
  { id: '4', position: { x: 300, y: 100 }, data: { label: '父节点2' }, type: 'custom' },
  { id: '5', position: { x: 300, y: 200 }, data: { label: '子节点2-1' } },
];

const initialEdges: Edge[] = [
  { id: 'e1-2', source: '1', target: '2' },
  { id: 'e1-3', source: '1', target: '3' },
  { id: 'e4-5', source: '4', target: '5' },
];

export default function Flow() {
  // 记录处于折叠状态的父节点ID集合
  const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set());
  const [nodes, setNodes] = useNodesState(initialNodes);
  const [edges, setEdges] = useEdgesState(initialEdges);

  // 递归获取指定节点的所有后代节点ID(含深层嵌套)
  const getAllDescendants = (nodeId: string, allNodes: Node[], allEdges: Edge[]): string[] => {
    const outgoers = getOutgoers({ id: nodeId } as Node, allNodes, allEdges);
    const descendantIds = outgoers.map(node => node.id);
    
    outgoers.forEach(child => {
      descendantIds.push(...getAllDescendants(child.id, allNodes, allEdges));
    });
    
    return descendantIds;
  };

  // 点击节点切换折叠状态
  const handleNodeClick = (_: ReactMouseEvent, node: Node) => {
    setCollapsedNodes(prev => {
      const updatedSet = new Set(prev);
      updatedSet.has(node.id) ? updatedSet.delete(node.id) : updatedSet.add(node.id);
      return updatedSet;
    });
  };

  // 根据折叠状态更新节点和边的hidden属性
  useEffect(() => {
    // 更新节点显示状态
    setNodes(prevNodes => 
      prevNodes.map(node => {
        const isHidden = [...collapsedNodes].some(parentId => 
          getAllDescendants(parentId, prevNodes, edges).includes(node.id)
        );
        return { ...node, hidden: isHidden };
      })
    );

    // 更新边的显示状态:关联节点任一隐藏则边隐藏
    setEdges(prevEdges => 
      prevEdges.map(edge => {
        const sourceNode = nodes.find(n => n.id === edge.source);
        const targetNode = nodes.find(n => n.id === edge.target);
        return { ...edge, hidden: sourceNode?.hidden || targetNode?.hidden };
      })
    );
  }, [collapsedNodes, nodes, edges, setNodes, setEdges]);

  // 自定义父节点组件,显示展开/折叠图标
  const CustomNode = ({ data, id }: { data: { label: string }, id: string }) => {
    const isCollapsed = collapsedNodes.has(id);
    return (
      <div style={{ padding: '8px 12px', border: '1px solid #ccc', borderRadius: '4px', backgroundColor: '#fff' }}>
        <span>{data.label}</span>
        <span 
          style={{ marginLeft: '8px', cursor: 'pointer', color: '#666' }}
        >
          {isCollapsed ? '+' : '-'}
        </span>
      </div>
    );
  };

  return (
    <div style={{ height: '100vh' }}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodeClick={handleNodeClick}
        nodeTypes={{ custom: CustomNode }}
      />
    </div>
  );
}

方案优势

  • 支持多层嵌套子节点的折叠/展开,递归逻辑覆盖全层级后代
  • 自动同步关联边的显示状态,保证流程图的视觉完整性
  • 状态管理简洁,用Set跟踪折叠状态,避免冗余判断
  • 自定义节点组件直观展示展开/折叠状态,提升交互体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 06:55:22