React Flow Implementation

基于 @xyflow/react 构建节点式流程图与可视化编辑器的完整实现指南。

已扫描
适合谁
前端开发者、可视化应用设计师
不适合谁
无 React 基础的用户、需要直接运行的非开发人员
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @anderskev/react-flow-implementation

Skill 说明

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

React Flow 实现

快速开始

import { useCallback } from 'react';
import { ReactFlow, useNodesState, useEdgesState, addEdge } from '@xyflow/react';
import '@xyflow/react/dist/style.css';

const initialNodes = [
  { id: '1', position: { x: 0, y: 0 }, data: { label: '节点 1' } },
  { id: '2', position: { x: 200, y: 100 }, data: { label: '节点 2' } },
];

const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];

export default function 流程图() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

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

  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        fitView
      />
    </div>
  );
}

实现检查项

按顺序执行;不要跳过“看起来没问题”的步骤。

  1. 页面样式加载通过条件:渲染 <ReactFlow /> 的打包文件导入了 @xyflow/react/dist/style.css(或等效的 CSS 处理流程),确保节点和连线可见,且点击区域与视觉元素匹配。
  2. **useReactFlow 作用域边界通过条件**:所有调用 useReactFlow() 的组件都是 <ReactFlowProvider> 的后代,且该提供者包裹的组件树与 <ReactFlow /> 相同(或你有意拆分状态并明确命名两个根节点)。
  3. **稳定的 nodeTypes / edgeTypes通过条件:这些映射必须是模块级常量**或使用 useMemo 并传入稳定依赖项——不能在渲染 <ReactFlow /> 的组件中每渲染一次就创建新的 { ... } 字面量。
  4. 处理程序匹配连线通过条件:对于具有多个 Handle id 的节点,所有必须连接到特定处理程序的连线都应正确设置 sourceHandle / targetHandle(或你有意接受默认处理程序解析)。

有关 MiniMap、Controls、Background、NodeToolbar 和 NodeResizer 模式的更多信息,请参阅 [ADDITIONAL_COMPONENTS.md](ADDITIONAL_COMPONENTS.md)。

核心模式

TypeScript 类型

import type { Node, Edge, NodeProps, BuiltInNode } from '@xyflow/react';

// 定义带有数据结构的自定义节点类型
type 自定义节点 = Node<{ value: number; label: string }, 'custom'>;

// 与内置节点组合
type 我的节点 = 自定义节点 | BuiltInNode;
type 我的连线 = Edge<{ weight?: number }>;

// 在整个应用中使用
const [nodes, setNodes] = useNodesState<我的节点>(initialNodes);

自定义节点

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

// 定义节点类型
type 计数器节点 = Node<{ count: number }, 'counter'>;

// 始终使用 memo 提升性能
const 计数器节点 = memo(function 计数器节点({ data, isConnectable }: NodeProps<计数器节点>) {
  return (
    <>
      <Handle type="target" position={Position.Top} isConnectable={isConnectable} />
      <div className="counter-node">
        数量:{data.count}
        {/* nodrag 防止在点击按钮时触发拖拽 */}
        <button className="nodrag" onClick={() => console.log('点击了')}>
          增加
        </button>
      </div>
      <Handle type="source" position={Position.Bottom} isConnectable={isConnectable} />
    </>
  );
});

// 在组件外部注册,避免重复渲染
const nodeTypes = { counter: 计数器节点 };

// 在 ReactFlow 中使用
<ReactFlow nodeTypes={nodeTypes} ... />

多个处理程序

// 当节点有多个同类型处理程序时,使用 handle ID
<Handle type="source" position={Position.Right} id="a" />
<Handle type="source" position={Position.Right} id="b" style={{ top: 20 }} />

// 使用特定处理程序连接
const edge = {
  id: 'e1-2',
  source: '1',
  sourceHandle: 'a',
  target: '2',
  targetHandle: null
};

自定义连线

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

function 自定义连线({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data }: EdgeProps) {
  const [edgePath, labelX, labelY] = getSmoothStepPath({
    sourceX, sourceY, sourcePosition,
    targetX, targetY, targetPosition,
  });

  return (
    <>
      <BaseEdge id={id} path={edgePath} />
      <text x={labelX} y={labelY} className="edge-label">{data?.label}</text>
    </>
  );
}

const edgeTypes = { custom: 自定义连线 };

状态管理

受控模式(生产环境推荐)

// 外部状态与变更处理器
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges);

const onNodesChange = useCallback(
  (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
  []
);

const onEdgesChange = useCallback(
  (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
  []
);

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

使用 useReactFlow

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

function 流程控制() {
  const {
    getNodes, setNodes, addNodes, updateNodeData,
    getEdges, setEdges, addEdges,
    fitView, zoomIn, zoomOut, setViewport,
    deleteElements, toObject,
  } = useReactFlow();

  const 添加节点 = () => {
    addNodes({ id: `${Date.now()}`, position: { x: 100, y: 100 }, data: { label: '新节点' } });
  };

  return <button onClick={添加节点}>添加节点</button>;
}

// 使用 useReactFlow 时必须包裹在 provider 内
function App() {
  return (
    <ReactFlowProvider>
      <流程图 />
      <流程控制 />
    </ReactFlowProvider>
  );
}

更新节点数据

const { updateNodeData } = useReactFlow();

// 合并现有数据
updateNodeData(节点ID, { label: '已更新' });

// 完全替换数据
updateNodeData(节点ID, { newField: 'value' }, { replace: true });

视口与自动适配

// 初始渲染时自动适配视图
<ReactFlow fitView fitViewOptions={{ padding: 0.2, maxZoom: 1 }} />

// 程序化控制
const { fitView, setViewport, getViewport, zoomTo } = useReactFlow();

// 适配特定节点
fitView({ nodes: [{ id: '1' }, { id: '2' }], duration: 500 });

// 设置精确的视口
setViewport({ x: 100, y: 100, zoom: 1.5 }, { duration: 300 });

连接验证

const isValidConnection = useCallback((connection: Connection) => {
  // 防止自连接
  if (connection.source === connection.target) return false;

  // 自定义验证逻辑
  const sourceNode = getNode(connection.source);
  const targetNode = getNode(connection.target);

  return sourceNode?.type !== targetNode?.type;
}, []);

<ReactFlow isValidConnection={isValidConnection} />

常用属性参考

<ReactFlow
  // 核心数据
  nodes={nodes}
  edges={edges}
  onNodesChange={onNodesChange}
  onEdgesChange={onEdgesChange}

  // 自定义类型(在组件外部定义)
  nodeTypes={nodeTypes}
  edgeTypes={edgeTypes}

  // 连接相关
  onConnect={onConnect}
  connectionMode={ConnectionMode.Loose}  // 允许目标到目标的连接
  isValidConnection={isValidConnection}

  // 视口设置
  fitView
  minZoom={0.1}
  maxZoom={4}
  defaultViewport={{ x: 0, y: 0, zoom: 1 }}

  // 交互行为
  nodesDraggable={true}
  nodesConnectable={true}
  elementsSelectable={true}
  panOnDrag={true}
  zoomOnScroll={true}

  // 附加组件
  <MiniMap />
  <Controls />
  <Background variant={BackgroundVariant.Dots} />
</ReactFlow>

用于交互的 CSS 类

类名效果
nodrag点击元素时禁止拖动
nowheel禁止通过鼠标滚轮缩放
nopan禁止通过元素触发平移
nokey禁止键盘事件(用于输入框)

附加组件

详见 [ADDITIONAL_COMPONENTS.md](ADDITIONAL_COMPONENTS.md),包含 MiniMap、Controls、Background、NodeToolbar、NodeResizer。

A
@anderskev

已收录 5 个 Skill

相关推荐