如何在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
相关产品推荐
相关产品推荐

