react-flow
React Flow(@xyflow/react)を使ったワークフローの可視化と、カスタムノード・エッジの構築を支援するスキルです。グラフの可視化、カスタムワークフローノードの作成、エッジラベルの実装、ビューポート制御などを行う際に活用できます。ReactFlow、@xyflow/react、Handle、NodeProps、EdgeProps、useReactFlow、fitView の使用時にトリガーされます。
description の原文を見る
React Flow (@xyflow/react) for workflow visualization with custom nodes and edges. Use when building graph visualizations, creating custom workflow nodes, implementing edge labels, or controlling viewport. Triggers on ReactFlow, @xyflow/react, Handle, NodeProps, EdgeProps, useReactFlow, fitView.
SKILL.md 本文
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: 'Input Node' },
position: { x: 250, y: 5 },
},
{
id: '2',
data: { label: 'Default Node' },
position: { x: 100, y: 100 },
},
{
id: '3',
type: 'output',
data: { label: 'Output Node' },
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: 'Node Label' },
style: { background: '#D6D5E6' },
className: 'custom-node',
};
組み込みノードタイプ:
default: 標準ノードinput: ターゲットハンドルなしoutput: ソースハンドルなしgroup: 他のノードのコンテナ
エッジ
エッジはノードを接続します。各エッジに必要な項目:
id: 一意の識別子source: ソースノードのIDtarget: ターゲットノードのID
import { Edge } from '@xyflow/react';
const edge: Edge = {
id: 'e1-2',
source: '1',
target: '2',
type: 'smoothstep',
animated: true,
label: 'Edge Label',
style: { stroke: '#fff', strokeWidth: 2 },
};
組み込みエッジタイプ:
default: ベジェ曲線straight: 直線step: 直角コーナーの直交smoothstep: 丸いコーナーの直交
ハンドル
ハンドルはノード上の接続ポイントです。Position enum を使用してプレースメントを指定します:
import { Handle, Position } from '@xyflow/react';
<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />
利用可能な位置: Position.Top, Position.Right, Position.Bottom, Position.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()}>Fit View</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"を追加してインプット/ボタンのドラッグを防止
詳細なパターン (スタイリング、航空地図ピン、動的ハンドル含む) は カスタムノード参照 を参照してください。
カスタムエッジ
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] を返します。
インタラクティブラベル
ポインターイベントを持つ HTML ベースのラベルに EdgeLabelRenderer を使用します:
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 テキストパターンについては カスタムエッジ参照 を参照してください。
ビューポート制御
プログラムによるビューポート制御に 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;
}
保存/復元状態、コントロールされたビューポート、座標変換については ビューポート参照 を参照してください。
イベント
React Flow は包括的なイベント処理を提供します:
ノードイベント
import { NodeMouseHandler, OnNodeDrag } from '@xyflow/react';
const onNodeClick: NodeMouseHandler = (event, node) => {
console.log('Node clicked:', node.id);
};
const onNodeDrag: OnNodeDrag = (event, node, nodes) => {
console.log('Dragging:', node.id);
};
<ReactFlow
onNodeClick={onNodeClick}
onNodeDrag={onNodeDrag}
onNodeDragStop={onNodeClick}
/>
エッジと接続イベント
import { EdgeMouseHandler, OnConnect } from '@xyflow/react';
const onEdgeClick: EdgeMouseHandler = (event, edge) => console.log('Edge:', edge.id);
const onConnect: OnConnect = (connection) => console.log('Connected:', connection);
<ReactFlow onEdgeClick={onEdgeClick} onConnect={onConnect} />
選択とビューポートイベント
import { useOnSelectionChange, useOnViewportChange } from '@xyflow/react';
useOnSelectionChange({
onChange: ({ nodes, edges }) => console.log('Selected:', nodes.length, edges.length),
});
useOnViewportChange({
onChange: (viewport) => console.log('Viewport:', viewport.zoom),
});
検証、削除、エラー処理を含む完全なイベントカタログについては イベント参照 を参照してください。
一般的なパターン
ドラッグ/パンの防止
<input className="nodrag" />
<button className="nodrag nopan">Click me</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: 'New' } }]);
};
ノードデータの更新
const { updateNodeData } = useReactFlow();
updateNodeData('node-1', { label: 'Updated' });
updateNodeData('node-1', (node) => ({ ...node.data, count: node.data.count + 1 }));
プロバイダーパターン
フロー外で useReactFlow() を使用する場合、アプリを ReactFlowProvider でラップします:
import { ReactFlow, ReactFlowProvider, useReactFlow } from '@xyflow/react';
function Controls() {
const { fitView } = useReactFlow(); // プロバイダー内にいる必要があります
return <button onClick={() => fitView()}>Fit View</button>;
}
function App() {
return (
<ReactFlowProvider>
<Controls />
<ReactFlow nodes={nodes} edges={edges} />
</ReactFlowProvider>
);
}
実装チェックポイント
統合が完了したと判断する前に、以下の順序付きチェックを使用します (スタイル設定ではなく一般的な落とし穴に焦点を当てます)。
- バンドルに含まれる CSS —
import '@xyflow/react/dist/style.css'がアプリ (エントリまたはレイアウト) で実行されることを確認します。成功: ノードとエッジは予期されたデフォルトスタイリングを持ち、ハンドルは表示されて操作できます。 - 安定した
nodeTypes/edgeTypes— 毎回レンダリングするたびに新しいオブジェクトリテラルを渡さないでください。マップをコンポーネント外で定義するか、useMemoと正しい依存関係でメモ化します。成功: 選択またはビューポートの変更時にリマウントフリッカーや「最大更新深度」/暴走更新がありません。 - プロバイダーバウンダリ —
useReactFlow()を呼び出すコンポーネントはReactFlowProviderの子孫である必要があり、フローは実際にマウントする必要があります。成功: コンテキストエラーがなく、プログラムによるAPI (fitViewなど) が予期される場所で機能します。
参照ファイル
詳細な実装パターンについては以下を参照してください:
カスタムノード- NodeProps タイプ、Handle コンポーネント、動的ハンドル、スタイリングパターンカスタムエッジ- EdgeProps タイプ、パスユーティリティ、EdgeLabelRenderer、アニメーションエッジビューポート- useReactFlow メソッド、fitView オプション、座標変換イベント- ノード/エッジ/接続イベント、選択処理、ビューポート変更
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- existential-birds
- ライセンス
- Apache-2.0
- 最終更新
- 不明
Source: https://github.com/existential-birds/beagle / ライセンス: Apache-2.0
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。