React Flow

基于 React Flow 构建可自定义节点与连线的流程图可视化工具。

已扫描
适合谁
前端开发者、UI/UX 设计师
不适合谁
无编程基础的普通用户、非技术类内容创作者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @anderskev/react-flow

Skill 说明

命令、参数、文件名以原文为准

React Flow

React Flow (@xyflow/react) 是一个用于构建基于节点的图表、工作流编辑器和交互式图表的库。它提供了一个高度可定制的框架,可用于创建可视化编程界面、流程图以及网络拓扑图。

快速入门

安装

pnpm add @xyflow/react

基础设置

import { ReactFlow, Node, Edge, Background, Controls, MiniMap } from '@xyflow/react';
import '@xyflow/react/dist/style.css';

const initialNodes: Node[] = [
  {
    id: '1',
    type: 'input',
    data: { label: '输入节点' },
    position: { x: 250, y: 5 },
  },
  {
    id: '2',
    data: { label: '默认节点' },
    position: { x: 100, y: 100 },
  },
  {
    id: '3',
    type: 'output',
    data: { label: '输出节点' },
    position: { x: 400, y: 100 },
  },
];

const initialEdges: Edge[] = [
  { id: 'e1-2', source: '1', target: '2', animated: true },
  { id: 'e2-3', source: '2', target: '3' },
];

function Flow() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow nodes={initialNodes} edges={initialEdges}>
        <Background />
        <Controls />
        <MiniMap />
      </ReactFlow>
    </div>
  );
}

export default Flow;

核心概念

节点

节点是图表的基本构建单元。每个节点包含:

  • id:唯一标识符
  • type:节点类型(内置或自定义)
  • position:{ x, y } 坐标
  • data:自定义数据对象
import { Node } from '@xyflow/react';

const node: Node = {
  id: 'node-1',
  type: 'default',
  position: { x: 100, y: 100 },
  data: { label: '节点标签' },
  style: { background: '#D6D5E6' },
  className: 'custom-node',
};

内置节点类型:

  • default:标准节点
  • input:无目标连接点
  • output:无源连接点
  • group:用于容纳其他节点的容器

边用于连接节点。每条边需要:

  • id:唯一标识符
  • source:源节点 ID
  • target:目标节点 ID
import { Edge } from '@xyflow/react';

const edge: Edge = {
  id: 'e1-2',
  source: '1',
  target: '2',
  type: 'smoothstep',
  animated: true,
  label: '边标签',
  style: { stroke: '#fff', strokeWidth: 2 },
};

内置边类型:

  • default:贝塞尔曲线
  • straight:直线
  • step:正交线,带尖角
  • smoothstep:正交线,带圆角

连接点

连接点是节点上的连接位置。使用 Position 枚举进行定位:

import { Handle, Position } from '@xyflow/react';

<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />

可用位置:Position.TopPosition.RightPosition.BottomPosition.Left

状态管理

受控图表

使用状态钩子实现完全控制:

import { useNodesState, useEdgesState, addEdge, OnConnect } from '@xyflow/react';
import { useCallback } from 'react';

function ControlledFlow() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  const onConnect: OnConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
    />
  );
}

useReactFlow 钩子

通过该钩子访问 React Flow 实例以进行程序化控制:

import { useReactFlow } from '@xyflow/react';

function FlowControls() {
  const {
    getNodes,
    getEdges,
    setNodes,
    setEdges,
    addNodes,
    addEdges,
    deleteElements,
    fitView,
    zoomIn,
    zoomOut,
    getNode,
    getEdge,
    updateNode,
    updateEdge,
  } = useReactFlow();

  return (
    <button onClick={() => fitView()}>适应视图</button>
  );
}

自定义节点

使用 NodeProps<T> 和类型化的数据定义自定义节点:

import { NodeProps, Node, Handle, Position } from '@xyflow/react';

export type CustomNode = Node<{ label: string; status: 'active' | 'inactive' }, 'custom'>;

function CustomNodeComponent({ data, selected }: NodeProps<CustomNode>) {
  return (
    <div className={`px-4 py-2 ${selected ? 'ring-2' : ''}`}>
      <Handle type="target" position={Position.Top} />
      <div className="font-bold">{data.label}</div>
      <Handle type="source" position={Position.Bottom} />
    </div>
  );
}

通过 nodeTypes 注册:

const nodeTypes: NodeTypes = { custom: CustomNodeComponent };
<ReactFlow nodeTypes={nodeTypes} />

关键模式

  • 多个连接点:使用 id 属性和 style 进行定位
  • 动态连接点:在添加或移除连接点后调用 useUpdateNodeInternals([nodeId])
  • 可交互元素:为输入框或按钮添加 className="nodrag" 以禁止拖拽

详见 [自定义节点参考](./references/custom-nodes.md),了解包括样式、航空地图标记和动态连接点在内的详细模式。

自定义边

使用 EdgeProps<T> 和路径工具定义自定义边:

import { BaseEdge, EdgeProps, getBezierPath } from '@xyflow/react';

export type CustomEdge = Edge<{ status: 'normal' | 'error' }, 'custom'>;

function CustomEdgeComponent(props: EdgeProps<CustomEdge>) {
  const [edgePath] = getBezierPath(props);

  return (
    <BaseEdge
      id={props.id}
      path={edgePath}
      style={{ stroke: props.data?.status === 'error' ? '#ef4444' : '#64748b' }}
    />
  );
}

路径工具

  • getBezierPath() - 平滑曲线
  • getStraightPath() - 直线
  • getSmoothStepPath() - 正交带圆角
  • getSmoothStepPath({ borderRadius: 0 }) - 正交带尖角(步进边)

所有方法返回 [path, labelX, labelY, offsetX, offsetY]

交互式标签

使用 EdgeLabelRenderer 实现支持指针事件的 HTML 标签。

# React Flow

## 自定义边(Custom Edges)

import { EdgeLabelRenderer, BaseEdge, getBezierPath } from '@xyflow/react';

function ButtonEdge(props: EdgeProps) {

const [edgePath, labelX, labelY] = getBezierPath(props);

return (

<>

<BaseEdge id={props.id} path={edgePath} />

<EdgeLabelRenderer>

<div

style={{

position: 'absolute',

transform: translate(-50%, -50%) translate(${labelX}px, ${labelY}px),

pointerEvents: 'all',

}}

className="nodrag nopan"

<button onClick={() => console.log('Delete')}>×</button>

</div>

</EdgeLabelRenderer>

</>

);

}

有关动画边、时间标签和 SVG 文本模式,请参阅 [自定义边参考](./references/custom-edges.md)。

## 视口控制(Viewport Control)

使用 `useReactFlow()` 钩子实现程序化视口控制:

import { useReactFlow } from '@xyflow/react';

function ViewportControls() {

const { fitView, zoomIn, zoomOut, setCenter, screenToFlowPosition } = useReactFlow();

// 将所有节点适配到视图中

const handleFitView = () => fitView({ padding: 0.2, duration: 400 });

// 缩放控制

const handleZoomIn = () => zoomIn({ duration: 300 });

const handleZoomOut = () => zoomOut({ duration: 300 });

// 定位到特定坐标

const handleCenter = () => setCenter(250, 250, { zoom: 1.5, duration: 500 });

// 将屏幕坐标转换为画布坐标

const addNodeAtClick = (event: React.MouseEvent) => {

const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });

// 使用 position 添加节点

};

return null;

}

有关保存/恢复状态、受控视口和坐标转换的详细信息,请参阅 [视口参考](./references/viewport.md)。

## 事件处理(Events)

React Flow 提供了全面的事件处理机制:

### 节点事件(Node Events)

import { NodeMouseHandler, OnNodeDrag } from '@xyflow/react';

const onNodeClick: NodeMouseHandler = (event, node) => {

console.log('节点被点击:', node.id);

};

const onNodeDrag: OnNodeDrag = (event, node, nodes) => {

console.log('正在拖动:', node.id);

};

<ReactFlow

onNodeClick={onNodeClick}

onNodeDrag={onNodeDrag}

onNodeDragStop={onNodeClick}

/>

### 边与连接事件(Edge and Connection Events)

import { EdgeMouseHandler, OnConnect } from '@xyflow/react';

const onEdgeClick: EdgeMouseHandler = (event, edge) => console.log('边:', edge.id);

const onConnect: OnConnect = (connection) => console.log('已连接:', connection);

<ReactFlow onEdgeClick={onEdgeClick} onConnect={onConnect} />

### 选择与视口事件(Selection and Viewport Events)

import { useOnSelectionChange, useOnViewportChange } from '@xyflow/react';

useOnSelectionChange({

onChange: ({ nodes, edges }) => console.log('已选中:', nodes.length, edges.length),

});

useOnViewportChange({

onChange: (viewport) => console.log('视口:', viewport.zoom),

});

完整事件列表,包括验证、删除和错误处理,请参阅 [事件参考](./references/events.md)。

## 常见使用模式(Common Patterns)

### 阻止拖拽/平移

<input className="nodrag" />

<button className="nodrag nopan">点击我</button>

### 连接有效性验证

const isValidConnection = (connection: Connection) => {

return connection.source !== connection.target; // 禁止自连接

};

<ReactFlow isValidConnection={isValidConnection} />

### 点击添加节点

const { screenToFlowPosition, setNodes } = useReactFlow();

const onPaneClick = (event: React.MouseEvent) => {

const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });

setNodes(nodes => [...nodes, { id: node-${Date.now()}, position, data: { label: '新建' } }]);

};

### 更新节点数据

const { updateNodeData } = useReactFlow();

updateNodeData('node-1', { label: '已更新' });

updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));

## Provider 模式

当在画布外部使用 `useReactFlow()` 时,需用 `ReactFlowProvider` 包裹应用:

import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';

function Controls() {

const { fitView } = useReactFlow(); // 必须在 provider 内部使用

return <button onClick={() => fitView()}>适配视图</button>;

}

function App() {

return (

<ReactFlowProvider>

<Controls />

<ReactFlow nodes={nodes} edges={edges} />

</ReactFlowProvider>

);

}

## 实现检查项(Implementation gates)

在认为集成已完成前,请按以下**顺序检查**(针对常见问题,非风格偏好):

1. **CSS 已打包** — 确保 `import '@xyflow/react/dist/style.css'` 在应用入口或布局中执行。
   **通过标准**:节点和边具有预期的默认样式;控制柄可见且可交互。

2. **稳定的 `nodeTypes` / `edgeTypes`** — 不要在每次渲染时传入新的对象字面量;将映射表定义在组件外,或使用 `useMemo` 并正确设置依赖项。
   **通过标准**:仅选择或视口变化时无重渲染闪烁或“最大更新深度”/无限更新问题。

3. **Provider 边界** — 调用 `useReactFlow()` 的组件必须是 `ReactFlowProvider` 的后代,且画布实际已挂载。
   **通过标准**:运行时无缺少上下文错误;程序化 API(如 `fitView`)在预期位置正常工作。

## 参考文件

更多实现模式详情请查阅:

- [自定义节点](./references/custom-nodes.md) - NodeProps 类型、Handle 组件、动态控制柄、样式模式
- [自定义边](./references/custom-edges.md) - EdgeProps 类型、路径工具函数、EdgeLabelRenderer、动画边
- [视口](./references/viewport.md) - useReactFlow 方法、fitView 选项、坐标转换
- [事件](./references/events.md) - 节点/边/连接事件、选择处理、视口变化
A
@anderskev

已收录 5 个 Skill

相关推荐