mcpField
R
react-flow-implementation

react-flow-implementation

github.com/existential-birds开发85364 次安装11 周前更新
代码仓库

关于 react-flow-implementation

react-flow-implementation 是用于使用 React Flow 实现界面的技能,提供代码模式和按序检查项,涵盖样式、Provider 边界、稳定的节点与边类型、连接句柄、状态和连线。

主要功能

  1. 01React Flow 配置模式
  2. 02样式与 Provider 检查
  3. 03稳定的节点和边类型
  4. 04连接句柄与边的匹配
  5. 05状态与连线模式

适用场景

  1. 01构建受控的节点与边编辑器
  2. 02添加自定义节点或边
  3. 03检查 Provider 和连接句柄配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/existential-birds/beagle --skill react-flow-implementation

安装后在 Claude Code 中输入/react-flow-implementation确认已加载。

附带文件ADDITIONAL_COMPONENTS.mdEDGE_PATHS.md

详细介绍

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: 'Node 1' } },
  { id: '2', position: { x: 200, y: 100 }, data: { label: 'Node 2' } },
];

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

export default function Flow() {
  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> 的后代,且该 Provider 包装的树与 <ReactFlow /> 位于同一棵树中(或者你有意拆分了 store,并且能够指出两个根节点)。
  3. 稳定的 nodeTypes / edgeTypes — 通过条件: 这些映射是模块级常量,或使用依赖稳定的 useMemo,而不是在渲染 <ReactFlow /> 的组件中每次渲染都创建新的 { ... } 字面量。
  4. 句柄与边匹配 — 通过条件: 对于具有多个 Handle id 的节点,每条必须连接到特定句柄的边都设置了相应的 sourceHandle / targetHandle(或者你有意接受默认句柄解析方式)。

有关 MiniMap、Controls、Background、NodeToolbar 和 NodeResizer 的模式,请参阅 ADDITIONAL_COMPONENTS.md。

核心模式

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

// Define custom node type with data shape
type CustomNode = Node<{ value: number; label: string }, 'custom'>;

// Combine with built-in nodes
type MyNode = CustomNode | BuiltInNode;
type MyEdge = Edge<{ weight?: number }>;

// Use throughout app
const [nodes, setNodes] = useNodesState<MyNode>(initialNodes);
自定义节点
import { memo } from 'react';
import { Handle, Position, type NodeProps } from '@xyflow/react';

// Define node type
type CounterNode = Node<{ count: number }, 'counter'>;

// Always wrap in memo for performance
const CounterNode = memo(function CounterNode({ data, isConnectable }: NodeProps<CounterNode>) {
  return (
    <>
      <Handle type="target" position={Position.Top} isConnectable={isConnectable} />
      <div className="counter-node">
        Count: {data.count}
        {/* nodrag prevents dragging when interacting with button */}
        <button className="nodrag" onClick={() => console.log('clicked')}>
          Increment
        </button>
      </div>
      <Handle type="source" position={Position.Bottom} isConnectable={isConnectable} />
    </>
  );
});

// Register in nodeTypes (define OUTSIDE component to avoid re-renders)
const nodeTypes = { counter: CounterNode };

// Use in ReactFlow
<ReactFlow nodeTypes={nodeTypes} ... />
多个句柄
// Use handle IDs when a node has multiple handles of same type
<Handle type="source" position={Position.Right} id="a" />
<Handle type="source" position={Position.Right} id="b" style={{ top: 20 }} />

// Connect with specific handles
const edge = {
  id: 'e1-2',
  source: '1',
  sourceHandle: 'a',
  target: '2',
  targetHandle: null
};
自定义边
import { BaseEdge, EdgeProps, getSmoothStepPath } from '@xyflow/react';

function CustomEdge({ 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: CustomEdge };

状态管理

受控模式(推荐用于生产环境)
// External state with change handlers
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 FlowControls() {
  const {
    getNodes, setNodes, addNodes, updateNodeData,
    getEdges, setEdges, addEdges,
    fitView, zoomIn, zoomOut, setViewport,
    deleteElements, toObject,
  } = useReactFlow();

  const addNode = () => {
    addNodes({ id: `${Date.now()}`, position: { x: 100, y: 100 }, data: { label: 'New' } });
  };

  return <button onClick={addNode}>Add Node</button>;
}

// Must wrap in provider when using useReactFlow
function App() {
  return (
    <ReactFlowProvider>
      <Flow />
      <FlowControls />
    </ReactFlowProvider>
  );
}
更新节点数据
const { updateNodeData } = useReactFlow();

// Merge with existing data
updateNodeData(nodeId, { label: 'Updated' });

// Replace data entirely
updateNodeData(nodeId, { newField: 'value' }, { replace: true });

视口与适应视图

// Fit on initial render
<ReactFlow fitView fitViewOptions={{ padding: 0.2, maxZoom: 1 }} />

// Programmatic control
const { fitView, setViewport, getViewport, zoomTo } = useReactFlow();

// Fit to specific nodes
fitView({ nodes: [{ id: '1' }, { id: '2' }], duration: 500 });

// Set exact viewport
setViewport({ x: 100, y: 100, zoom: 1.5 }, { duration: 300 });

连接验证

const isValidConnection = useCallback((connection: Connection) => {
  // Prevent self-connections
  if (connection.source === connection.target) return false;

  // Custom validation logic
  const sourceNode = getNode(connection.source);
  const targetNode = getNode(connection.target);

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

<ReactFlow isValidConnection={isValidConnection} />

常用属性参考

<ReactFlow
  // Core data
  nodes={nodes}
  edges={edges}
  onNodesChange={onNodesChange}
  onEdgesChange={onEdgesChange}

  // Custom types (define OUTSIDE component)
  nodeTypes={nodeTypes}
  edgeTypes={edgeTypes}

  // Connections
  onConnect={onConnect}
  connectionMode={ConnectionMode.Loose}  // Allow target-to-target
  isValidConnection={isValidConnection}

  // Viewport
  fitView
  minZoom={0.1}
  maxZoom={4}
  defaultViewport={{ x: 0, y: 0, zoom: 1 }}

  // Interaction
  nodesDraggable={true}
  nodesConnectable={true}
  elementsSelectable={true}
  panOnDrag={true}
  zoomOnScroll={true}

  // Additional components
  <MiniMap />
  <Controls />
  <Background variant={BackgroundVariant.Dots} />
</ReactFlow>

用于交互的 CSS 类

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

其他组件

有关 MiniMap、Controls、Background、NodeToolbar、NodeResizer,请参阅 ADDITIONAL_COMPONENTS.md。

评论

支持 Markdown