ReactFlow基础Skill reactflow-fundamentals

React Flow 基础技能是一个用于前端开发的 React 库,专注于构建可自定义的节点图编辑器、流程图、工作流界面和交互图表。涵盖核心概念、安装设置、节点和边配置、事件处理及插件组件,适用于数据可视化、无代码工具和 UI 设计。关键词:React Flow, 节点图表, 流程图, 交互界面, 前端开发, 数据可视化, 工作流编辑器

前端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称:reactflow-fundamentals 用户可调用:false 描述:使用 React Flow 构建基于节点的用户界面、流程图、工作流编辑器或交互图表时使用。涵盖设置、节点、边、控件和交互性。 允许工具:

  • Bash
  • Read

React Flow 基础

使用 React Flow 构建可自定义的节点编辑器和交互图表。 本技能涵盖核心概念、设置和创建流程界面的常见模式。

安装

# npm
npm install @xyflow/react

# pnpm
pnpm add @xyflow/react

# yarn
yarn add @xyflow/react

基本设置

import { useCallback } from 'react';
import {
  ReactFlow,
  useNodesState,
  useEdgesState,
  addEdge,
  Background,
  Controls,
  MiniMap,
  type Node,
  type Edge,
  type OnConnect,
} from '@xyflow/react';

import '@xyflow/react/dist/style.css';

const initialNodes: Node[] = [
  {
    id: '1',
    type: 'input',
    data: { label: '开始' },
    position: { x: 250, y: 0 },
  },
  {
    id: '2',
    data: { label: '处理' },
    position: { x: 250, y: 100 },
  },
  {
    id: '3',
    type: 'output',
    data: { label: '结束' },
    position: { x: 250, y: 200 },
  },
];

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

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

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

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

节点类型

内置节点类型

const nodes: Node[] = [
  // 输入节点 - 仅具有源句柄
  {
    id: '1',
    type: 'input',
    data: { label: '输入节点' },
    position: { x: 0, y: 0 },
  },
  // 默认节点 - 具有源和目标句柄
  {
    id: '2',
    type: 'default',
    data: { label: '默认节点' },
    position: { x: 0, y: 100 },
  },
  // 输出节点 - 仅具有目标句柄
  {
    id: '3',
    type: 'output',
    data: { label: '输出节点' },
    position: { x: 0, y: 200 },
  },
];

节点配置

const node: Node = {
  id: '唯一-id',
  type: 'default',
  position: { x: 100, y: 100 },
  data: { label: '我的节点', customProp: '值' },
  // 可选属性
  style: { backgroundColor: '#f0f0f0' },
  className: '自定义节点',
  sourcePosition: Position.Right,
  targetPosition: Position.Left,
  draggable: true,
  selectable: true,
  connectable: true,
  deletable: true,
  hidden: false,
  selected: false,
  dragging: false,
  zIndex: 0,
  extent: 'parent', // 约束到父节点
  parentId: '父节点-id', // 用于嵌套节点
  expandParent: true, // 当节点超出边界时扩展父节点
};

边类型

内置边类型

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

const edges: Edge[] = [
  // 默认边(贝塞尔曲线)
  {
    id: 'e1',
    source: '1',
    target: '2',
    type: 'default',
  },
  // 直线
  {
    id: 'e2',
    source: '2',
    target: '3',
    type: 'straight',
  },
  // 阶梯边(直角)
  {
    id: 'e3',
    source: '3',
    target: '4',
    type: 'step',
  },
  // 平滑阶梯边(圆角)
  {
    id: 'e4',
    source: '4',
    target: '5',
    type: 'smoothstep',
  },
];

边配置

const edge: Edge = {
  id: '边-id',
  source: '源节点-id',
  target: '目标节点-id',
  // 可选属性
  type: 'smoothstep',
  sourceHandle: '句柄-a',
  targetHandle: '句柄-b',
  label: '边标签',
  labelStyle: { fill: '#333', fontWeight: 700 },
  labelBgStyle: { fill: '#fff' },
  labelBgPadding: [8, 4],
  labelBgBorderRadius: 4,
  style: { stroke: '#333', strokeWidth: 2 },
  animated: true,
  markerEnd: {
    type: MarkerType.ArrowClosed,
    color: '#333',
  },
  markerStart: {
    type: MarkerType.Arrow,
  },
  interactionWidth: 20,
  deletable: true,
  selectable: true,
  selected: false,
  hidden: false,
  zIndex: 0,
  data: { customProp: '值' },
};

句柄

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

function CustomNode({ data }: NodeProps) {
  return (
    <div className="自定义节点">
      {/* 目标句柄(输入) */}
      <Handle
        type="target"
        position={Position.Top}
        id="输入"
        style={{ background: '#555' }}
        isConnectable={true}
      />

      <div>{data.label}</div>

      {/* 多个源句柄 */}
      <Handle
        type="source"
        position={Position.Bottom}
        id="输出-a"
        style={{ left: '25%', background: '#555' }}
      />
      <Handle
        type="source"
        position={Position.Bottom}
        id="输出-b"
        style={{ left: '75%', background: '#555' }}
      />
    </div>
  );
}

插件组件

背景

import { Background, BackgroundVariant } from '@xyflow/react';

<ReactFlow nodes={nodes} edges={edges}>
  {/* 点图案 */}
  <Background variant={BackgroundVariant.Dots} gap={12} size={1} />

  {/* 线图案 */}
  <Background variant={BackgroundVariant.Lines} gap={20} />

  {/* 交叉图案 */}
  <Background variant={BackgroundVariant.Cross} gap={25} />

  {/* 自定义样式 */}
  <Background
    color="#aaa"
    gap={16}
    size={1}
    variant={BackgroundVariant.Dots}
  />
</ReactFlow>

控件

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

<ReactFlow nodes={nodes} edges={edges}>
  <Controls
    showZoom={true}
    showFitView={true}
    showInteractive={true}
    position="bottom-left"
  />
</ReactFlow>

迷你地图

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

<ReactFlow nodes={nodes} edges={edges}>
  <MiniMap
    nodeColor={(node) => {
      switch (node.type) {
        case 'input':
          return '#0041d0';
        case 'output':
          return '#ff0072';
        default:
          return '#1a192b';
      }
    }}
    nodeStrokeWidth={3}
    zoomable
    pannable
  />
</ReactFlow>

面板

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

<ReactFlow nodes={nodes} edges={edges}>
  <Panel position="top-left">
    <button onClick={onSave}>保存</button>
    <button onClick={onRestore}>恢复</button>
  </Panel>

  <Panel position="top-right">
    <div>节点数量:{nodes.length}</div>
  </Panel>
</ReactFlow>

事件处理

import {
  ReactFlow,
  type NodeMouseHandler,
  type EdgeMouseHandler,
  type OnSelectionChangeFunc,
} from '@xyflow/react';

function Flow() {
  // 节点事件
  const onNodeClick: NodeMouseHandler = useCallback((event, node) => {
    console.log('节点点击:', node.id);
  }, []);

  const onNodeDoubleClick: NodeMouseHandler = useCallback((event, node) => {
    console.log('节点双击:', node.id);
  }, []);

  const onNodeDragStart: NodeMouseHandler = useCallback((event, node) => {
    console.log('拖动开始:', node.id);
  }, []);

  const onNodeDrag: NodeMouseHandler = useCallback((event, node) => {
    console.log('拖动中:', node.position);
  }, []);

  const onNodeDragStop: NodeMouseHandler = useCallback((event, node) => {
    console.log('拖动停止:', node.position);
  }, []);

  // 边事件
  const onEdgeClick: EdgeMouseHandler = useCallback((event, edge) => {
    console.log('边点击:', edge.id);
  }, []);

  // 选择变化
  const onSelectionChange: OnSelectionChangeFunc = useCallback(
    ({ nodes, edges }) => {
      console.log('选中的节点:', nodes);
      console.log('选中的边:', edges);
    },
    []
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodeClick={onNodeClick}
      onNodeDoubleClick={onNodeDoubleClick}
      onNodeDragStart={onNodeDragStart}
      onNodeDrag={onNodeDrag}
      onNodeDragStop={onNodeDragStop}
      onEdgeClick={onEdgeClick}
      onSelectionChange={onSelectionChange}
    />
  );
}

视口控制

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

function ViewportControls() {
  const { zoomIn, zoomOut, fitView, setCenter, setViewport, getViewport } =
    useReactFlow();

  return (
    <div>
      <button onClick={() => zoomIn()}>放大</button>
      <button onClick={() => zoomOut()}>缩小</button>
      <button onClick={() => fitView({ padding: 0.2 })}>适应视图</button>
      <button onClick={() => setCenter(0, 0, { zoom: 1 })}>居中</button>
      <button
        onClick={() => {
          const viewport = getViewport();
          console.log('当前视口:', viewport);
        }}
      >
        日志视口
      </button>
    </div>
  );
}

// 必须在 ReactFlowProvider 内部使用
function App() {
  return (
    <ReactFlowProvider>
      <Flow />
      <ViewportControls />
    </ReactFlowProvider>
  );
}

使用 useReactFlow 的节点操作

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

function NodeOperations() {
  const { getNodes, setNodes, getNode, addNodes, deleteElements } =
    useReactFlow();

  const addNewNode = () => {
    const newNode: Node = {
      id: `node-${Date.now()}`,
      data: { label: '新节点' },
      position: { x: Math.random() * 300, y: Math.random() * 300 },
    };
    addNodes(newNode);
  };

  const updateNode = (id: string, data: object) => {
    setNodes((nodes) =>
      nodes.map((node) =>
        node.id === id ? { ...node, data: { ...node.data, ...data } } : node
      )
    );
  };

  const deleteNode = (id: string) => {
    deleteElements({ nodes: [{ id }] });
  };

  const getAllNodes = () => {
    const nodes = getNodes();
    console.log('所有节点:', nodes);
  };

  return (
    <div>
      <button onClick={addNewNode}>添加节点</button>
      <button onClick={getAllNodes}>日志节点</button>
    </div>
  );
}

保存和恢复状态

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

function SaveRestore() {
  const { toObject, setNodes, setEdges, setViewport } = useReactFlow();

  const onSave = useCallback(() => {
    const flow = toObject();
    localStorage.setItem('flow', JSON.stringify(flow));
  }, [toObject]);

  const onRestore = useCallback(() => {
    const restoreFlow = async () => {
      const flow = JSON.parse(
        localStorage.getItem('flow') || '{}'
      ) as ReactFlowJsonObject;

      if (flow.nodes && flow.edges) {
        setNodes(flow.nodes);
        setEdges(flow.edges);
        if (flow.viewport) {
          setViewport(flow.viewport);
        }
      }
    };

    restoreFlow();
  }, [setNodes, setEdges, setViewport]);

  return (
    <Panel position="top-right">
      <button onClick={onSave}>保存</button>
      <button onClick={onRestore}>恢复</button>
    </Panel>
  );
}

何时使用此技能

使用 reactflow-fundamentals 当您需要:

  • 构建工作流构建器或无代码编辑器
  • 创建数据管道可视化
  • 设计状态机图
  • 构建聊天机器人对话流
  • 创建组织结构图
  • 设计电路图
  • 构建机器学习管道可视化器
  • 创建交互式决策树

最佳实践

  • 为节点和边使用唯一 ID
  • 使用 useCallback 记忆回调
  • 使用 TypeScript 以确保类型安全
  • 保持节点组件纯净且高效
  • 使用 CSS 类而非内联样式进行复杂样式
  • 将流状态存储在中央状态管理器以处理复杂应用
  • 在初始渲染时使用 fitView() 以提升用户体验
  • 为常见操作添加键盘快捷键
  • 实现撤销/重做以改善用户体验
  • 在连接前进行节点验证

资源