はい、承知いたしました。「ゼロからわかるReact Flow:インストールから基本機能まで」と題した、詳細な説明を含む約5000語の記事を作成し、直接表示します。
ゼロからわかるReact Flow:インストールから基本機能まで
はじめに
複雑なシステム、データフロー、思考プロセスなどを視覚的に表現する際に、フロー図(フローチャート)は非常に強力なツールです。Webアプリケーション上でインタラクティブなフロー図を作成したいと考えたとき、Reactを使っているなら「React Flow」は非常に有力な選択肢となります。
React Flowは、Reactのために設計された非常に柔軟でカスタマイズ可能なライブラリで、ノード、エッジ、そしてそれらのインタラクションを簡単に扱うことができます。ドラッグ&ドロップによるノードの移動、ズーム、パン、エッジによる接続など、フロー図に必要な基本的な機能が豊富に揃っています。さらに、カスタムノードやカスタムエッジを作成する機能も提供されており、アプリケーションの要件に合わせて自由に外観や挙動を変更できます。
この「ゼロからわかるReact Flow」記事では、プログラミング初心者やReact Flowを初めて触る方を対象に、React Flowのインストールから基本的な機能の使い方までを、豊富なコード例とともに詳細に解説します。この一歩を踏み出せば、あなたのアプリケーションに強力な視覚化機能を追加できるようになるでしょう。
この記事で学ぶこと:
- React Flowのインストール方法
- React Flowの基本的な概念(ノード、エッジ、ビューポートなど)
ReactFlow
コンポーネントの基本的な使い方- ノードとエッジの追加、更新、削除の方法
- 状態管理のためのReact Flowフック(
useNodesState
,useEdgesState
,useOnNodesChange
,useOnEdgesChange
,useOnConnect
など) - 基本的なインタラクション(ドラッグ、選択、接続)の実現方法
- ビューポートの制御(ズーム、パン、フィット)
- ヘルパーコンポーネント(
Controls
,Background
,MiniMap
)の使い方 - 基本的なカスタムノードの作成方法
対象読者:
- Reactでの開発経験が少しある方
- フロー図やグラフ構造をWebアプリケーションで表示したい方
- React Flowに興味があるが、どこから始めればいいか分からない方
- プログラミングは始めたばかりだが、視覚化に挑戦したい方
この記事は、React Flowの公式ドキュメントを読む前の準備としても最適です。さあ、React Flowの世界に飛び込んでみましょう!
1. React Flowとは?
React Flowは、Web上でグラフ、ツリー、フロー図などをレンダリングするためのライブラリです。Reactのエコシステム内で動作するように設計されており、Hooks APIやFunctional Componentsとの相性が抜群です。
React Flowの主な特徴:
- Declarative: Reactの宣言的なUI構築のアプローチに合わせて設計されています。ノードやエッジのデータ配列を渡すだけで、UIがレンダリングされます。
- Interactive: ノードのドラッグ、ズーム、パン、エッジの接続など、基本的なインタラクション機能が標準で提供されています。
- Customizable: 標準のノードやエッジだけでなく、完全にカスタマイズされたノードやエッジを作成できます。これにより、アプリケーションの特定のニーズに合わせた見た目や動作を実現できます。
- Hooks API: 状態管理やインスタンスへのアクセスにReact Hooksを使用します。これにより、関数コンポーネント内での記述が非常に簡潔になります。
- Performance: 大規模なグラフでもパフォーマンスを維持できるように設計されています。
React Flowは、ワークフローエディタ、データパイプラインの可視化、組織図、状態遷移図など、様々な種類の図を描画するのに適しています。
2. 開発環境の準備
React Flowを使用するには、基本的なReact開発環境が必要です。以下のものが揃っているか確認してください。
- Node.jsとnpmまたはYarn: Reactプロジェクトをビルドし、ライブラリを管理するために必要です。Node.jsの公式サイトからインストールできます。
- Reactプロジェクト: 新しいReactプロジェクトを作成するか、既存のプロジェクトを使用します。Create React App, Next.js, Viteなど、お好みの方法で作成できます。
- 基本的なReactの知識: コンポーネント、JSX、State、Props、Hooksといった基本的な概念を理解しているとスムーズに進められます。
まだReactプロジェクトがない場合は、Viteを使って素早く作成することをお勧めします。
“`bash
プロジェクトを作成(my-react-flow-app は任意のプロジェクト名)
npm create vite@latest my-react-flow-app –template react
プロジェクトディレクトリに移動
cd my-react-flow-app
依存関係をインストール
npm install
または
yarn install
開発サーバーを起動
npm run dev
または
yarn dev
“`
これで、React Flowをインストールする準備ができました。
3. React Flowのインストール
Reactプロジェクトの準備ができたら、次にReact Flowライブラリをインストールします。npmまたはYarnを使って、プロジェクトのルートディレクトリで以下のコマンドを実行します。
“`bash
npmを使用する場合
npm install reactflow
Yarnを使用する場合
yarn add reactflow
“`
これでReact Flowのコアライブラリがインストールされました。React Flowを使用する準備が整いました。
4. はじめてのReact Flowコンポーネント
React Flowを使う最も基本的なステップは、ReactFlow
コンポーネントをアプリケーションに配置することです。このコンポーネントが、フロー図を描画するためのキャンバス(Pane)を提供します。
ReactFlow
コンポーネントを使用するには、以下の要素が必要です。
ReactFlow
コンポーネント本体: フロー図を描画するメインのコンポーネントです。- ノードのデータ配列: 表示したい各ノードに関する情報(ID, 位置, タイプ, データなど)を含む配列です。
- エッジのデータ配列: ノード間の接続(エッジ)に関する情報(ID, 始点ノードID, 終点ノードID, タイプなど)を含む配列です。
- CSSスタイル: React Flowの基本スタイルを適用するために、CSSファイルをインポートする必要があります。
- 状態管理のためのフック: ノードやエッジの状態(位置の変更、選択など)を管理し、ユーザーの操作に応答するために、React Flowが提供するフック(
useNodesState
,useEdgesState
など)を使用します。 ReactFlowProvider
(オプションだが推奨): アプリケーション全体でReact Flowのコンテキストを共有するために、通常、React Flowコンポーネメントを含むアプリケーションのルートまたは親コンポーネントをReactFlowProvider
でラップします。これは、複数のReact Flowインスタンスを扱ったり、特定のフックを使用したりする場合に特に重要になります。
これらの要素を使って、簡単なフロー図を表示してみましょう。src/App.jsx
または src/App.tsx
を編集します。
まず、必要なモジュールをインポートします。
“`jsx
import React from ‘react’;
import ReactFlow, {
ReactFlowProvider, // プロバイダー
addEdge, // エッジ追加ヘルパー
useNodesState, // ノード状態フック
useEdgesState, // エッジ状態フック
Controls, // ズームなどのコントロール
MiniMap, // ミニマップ
Background, // 背景
} from ‘reactflow’;
// デフォルトスタイルをインポート
import ‘reactflow/dist/style.css’;
“`
次に、表示するノードとエッジの初期データを定義します。
“`jsx
// ノードの初期データ
const initialNodes = [
{
id: ‘1’, // ユニークなID
position: { x: 100, y: 100 }, // 位置
data: { label: ‘ノード 1’ }, // ノード内に表示するデータ
type: ‘input’, // ノードのタイプ (入力ノード)
},
{
id: ‘2’,
position: { x: 100, y: 200 },
data: { label: ‘ノード 2’ },
type: ‘default’, // デフォルトノード
},
{
id: ‘3’,
position: { x: 300, y: 200 },
data: { label: ‘ノード 3’ },
type: ‘output’, // 出力ノード
},
];
// エッジの初期データ
const initialEdges = [
{
id: ‘e1-2’, // ユニークなID
source: ‘1’, // 始点ノードのID
target: ‘2’, // 終点ノードのID
animated: true, // エッジをアニメーション表示
label: ‘to ノード 2’, // エッジのラベル
},
{
id: ‘e2-3’,
source: ‘2’,
target: ‘3’,
},
];
“`
id
: ノードまたはエッジを一意に識別するための文字列です。必須です。
position
: ノードが表示されるキャンバス上の { x, y }
座標です。ノードに必須です。
data
: ノードの内部に表示されるコンテンツなどを保持するためのオブジェクトです。任意のデータを格納できます。{ label: '...' }
はデフォルトノードがラベルを表示するために使用します。
type
: ノードまたはエッジの表示タイプを指定します。React Flowには 'input'
, 'default'
, 'output'
のノードタイプと、'default'
, 'straight'
, 'step'
, 'smoothstep'
のエッジタイプが組み込まれています。カスタムタイプを指定することもできます。
source
: エッジの始点となるノードの id
です。エッジに必須です。
target
: エッジの終点となるノードの id
です。エッジに必須です。
sourceHandle
, targetHandle
: (後述) ノード上の特定の位置からエッジを接続したい場合に使用します。
animated
: エッジに点線のアニメーションを付けるか指定します。
label
: エッジ上に表示されるテキストです。
次に、これらの初期データを使って状態を管理し、ReactFlow
コンポーネントをレンダーするコンポーネントを作成します。
“`jsx
// フロー図を表示するコンポーネント
const Flow = () => {
// useNodesState: ノードの状態を管理するフック
// initialNodes を初期値として設定
// nodes: 現在のノード配列
// setNodes: ノード配列を更新する関数
// onNodesChange: ノードの変更イベントが発生したときに呼び出されるハンドラ
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
// useEdgesState: エッジの状態を管理するフック
// initialEdges を初期値として設定
// edges: 現在のエッジ配列
// setEdges: エッジ配列を更新する関数
// onEdgesChange: エッジの変更イベントが発生したときに呼び出されるハンドラ
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
// useOnConnect: 新しいエッジが作成されたときに呼び出されるハンドラ
// onConnect は、ドラッグしてノード間を接続する際に React Flow によって呼び出されます。
// connection オブジェクト({ source, target, sourceHandle, targetHandle } を含む)を受け取ります。
// addEdge ヘルパー関数は、既存のエッジ配列と新しい connection オブジェクトを受け取り、
// 新しいエッジを追加した新しい配列を返します。
const onConnect = (connection) => {
// setEdges を呼び出して、新しいエッジを追加した後のエッジ配列で状態を更新
setEdges((eds) => addEdge(connection, eds));
};
return (
// ReactFlow コンポーネントを表示するコンテナ。高さが必要。
{/ React Flow に便利な追加UIコンポーネント /}
);
};
// アプリケーションのルートコンポーネント
// ReactFlowProvider でラップすることで、Flow コンポーネント内で React Flow のフックを使用できるようになる
const App = () => {
return (
);
};
export default App;
“`
コード解説:
- インポート: 必要なReact Flowコンポーネント、フック、ヘルパー関数、そしてスタイルシートをインポートします。
initialNodes
,initialEdges
: フロー図が最初に表示されるときに使うノードとエッジのデータを定義します。これは単なるJavaScriptの配列です。Flow
コンポーネント:useNodesState(initialNodes)
とuseEdgesState(initialEdges)
を使って、ノードとエッジの状態を管理します。これらのフックは、状態変数(nodes
,edges
)、状態を更新するためのセッター関数(setNodes
,setEdges
)、そしてReact Flowの内部的な変更イベント(ドラッグ、選択など)を処理するためのハンドラ関数(onNodesChange
,onEdgesChange
)を返します。onConnect = (connection) => { setEdges((eds) => addEdge(connection, eds)); }
は、ユーザーがノード間でエッジをドラッグして接続を確立したときに呼び出される関数です。addEdge
はReact Flowが提供するユーティリティ関数で、既存のエッジ配列に新しい接続情報(connection
オブジェクト)を追加した新しい配列を作成してくれます。この新しい配列をsetEdges
に渡すことで、Reactの状態が更新され、新しいエッジが画面に描画されます。<div style={{ width: '100%', height: '500px' }}>
は、ReactFlow
コンポーネントを表示するためのコンテナです。ReactFlow
は親要素のサイズいっぱいに広がるため、親要素に明示的なサイズ(ここでは高さ500px
)を与える必要があります。<ReactFlow ...>
: これがメインのコンポーネントです。nodes={nodes}
とedges={edges}
で、現在のノードとエッジのデータを渡します。onNodesChange={onNodesChange}
とonEdgesChange={onEdgesChange}
で、React Flow内部で発生したノードやエッジの状態変更イベントを処理するためのハンドラ関数を渡します。これにより、ノードのドラッグや選択状態の変更が正しく反映されます。onConnect={onConnect}
で、新しいエッジが作成されたときに呼び出されるハンドラ関数を渡します。fitView
プロパティは、コンポーネントがマウントされた直後に、全てのノードとエッジが画面内に収まるように自動的にズームとパンを調整する便利な機能です。
<Controls />
,<MiniMap />
,<Background />
: これらはReact Flowに同梱されている便利なUIコンポーネントです。ReactFlow
コンポーネントの子要素として配置することで、フロー図上に描画されます。
App
コンポーネント:ReactFlowProvider
でFlow
コンポーネントをラップしています。これはReact Flowのコンテキストを提供し、Flow
コンポーネントやその子孫コンポーネント内でuseNodesState
やuseEdgesState
などのフックを使用できるようにするために必要です。単一のフロー図しか扱わない場合でも、将来的な拡張やカスタム機能の実装を考えると、プロバイダーでラップしておくことが推奨されます。
このコードを実行すると、3つのノードと2つのエッジを持つ基本的なフロー図が表示されるはずです。ノードをドラッグしたり、ズームやパンをしたり、Controlsコンポーネントのボタンを使ったり、MiniMapを確認したり、背景グリッドを見たりすることができます。また、ノードのハンドル(デフォルトノードの場合はノードの上下左右中央、input/outputノードの場合は側面中央)からドラッグして、別のノードのハンドルにドロップすることで、新しいエッジを作成することも試してみてください。onConnect
ハンドラが呼び出され、新しいエッジが追加されます。
5. 基本的な概念の深掘り
最初の例で触れた基本的な概念について、さらに詳しく見ていきましょう。
5.1. ノード (Nodes)
ノードはフロー図の基本的な構成要素であり、データや処理などの個々のステップを表します。各ノードは一意のIDを持ち、位置、表示内容、タイプなどを設定できます。
ノードオブジェクトの主なプロパティ:
id
(string): 必須。ノードを一意に識別するID。position
({ x: number, y: number }): 必須。キャンバス上のノードの左上隅の座標。data
(object): 必須。ノード内に表示されるコンテンツや、ノードに関連付けたい任意のデータ。デフォルトノードはdata.label
を表示します。type
(string): ノードのタイプ。デフォルトは'default'
。'input'
,'output'
など組み込みのタイプやカスタムタイプを指定できます。className
(string): ノードに適用するCSSクラス名。style
(object): ノードの要素に直接適用するインラインCSSスタイルオブジェクト。sourcePosition
(Position): エッジの始点ハンドルが配置されるノード側の位置('top'
,'right'
,'bottom'
,'left'
)。指定しない場合は、エッジの接続に応じて自動的に決まります。targetPosition
(Position): エッジの終点ハンドルが配置されるノード側の位置('top'
,'right'
,'bottom'
,'left'
)。指定しない場合は、エッジの接続に応じて自動的に決まります。hidden
(boolean): ノードを非表示にするか。selected
(boolean): ノードが現在選択されているか(React Flowによって管理)。dragging
(boolean): ノードが現在ドラッグされているか(React Flowによって管理)。draggable
(boolean): ノードをドラッグ可能にするか(デフォルトはtrue
)。selectable
(boolean): ノードを選択可能にするか(デフォルトはtrue
)。connectable
(boolean): このノードから新しいエッジを作成できるか(デフォルトはtrue
)。deletable
(boolean): このノードを削除可能にするか(デフォルトはtrue
)。dragHandle
(string): ドラッグをトリガーするためにクリックする必要がある要素のセレクター(例:.drag-handle
)。指定しない場合はノード全体がドラッグ可能です。parentNode
(string): このノードが子ノードである場合の親ノードのID。グループ化などに使用します。extent
(‘parent’ | Rect): 親ノードの境界内('parent'
)、または指定された矩形(Rect
)内にノードの移動を制限します。width
,height
(number): ノードの幅と高さ。デフォルトノードは内容に基づいて自動的に決まりますが、明示的に指定することもできます。カスタムノードでは通常、CSSでサイズを制御します。
例:より詳細なノード定義
javascript
const myNodes = [
{
id: 'A',
position: { x: 50, y: 50 },
data: { label: 'プロセス開始' },
type: 'input',
style: { backgroundColor: '#d9f99d', color: '#333', border: '1px solid #22c55e', borderRadius: '5px' },
draggable: false, // ドラッグ不可にする
},
{
id: 'B',
position: { x: 300, y: 50 },
data: { label: 'データ処理' },
type: 'default',
className: 'my-custom-node', // CSSクラスを追加
},
{
id: 'C',
position: { x: 300, y: 200 },
data: { label: '出力結果' },
type: 'output',
// ノードの幅と高さを明示的に指定 (カスタムノードで役立つことが多い)
width: 150,
height: 50,
},
];
5.2. エッジ (Edges)
エッジはノード間の関係や接続を表します。各エッジは一意のIDを持ち、どのノードからどのノードへ接続されているかを指定します。
エッジオブジェクトの主なプロパティ:
id
(string): 必須。エッジを一意に識別するID。source
(string): 必須。始点ノードのID。target
(string): 必須。終点ノードのID。sourceHandle
(string): 始点ノード上の特定のハンドルのID(ハンドルを使用する場合)。targetHandle
(string): 終点ノード上の特定のハンドルのID(ハンドルを使用する場合)。type
(string): エッジのタイプ。デフォルトは'default'
。'straight'
,'step'
,'smoothstep'
など組み込みのタイプやカスタムタイプを指定できます。label
(string | ReactNode): エッジ上に表示されるラベル。文字列またはReact要素を指定できます。labelStyle
(object): エッジラベルの要素に適用するインラインCSSスタイル。labelShowBg
(boolean): エッジラベルの背景を表示するか(デフォルトはtrue
)。labelBgStyle
(object): エッジラベルの背景に適用するインラインCSSスタイル。labelBgPadding
([number, number]): エッジラベルの背景のパディング ([x, y])。labelBgBorderRadius
(number): エッジラベルの背景の角丸の半径。animated
(boolean): エッジに点線のアニメーションを付けるか。hidden
(boolean): エッジを非表示にするか。selected
(boolean): エッジが現在選択されているか(React Flowによって管理)。selectable
(boolean): エッジを選択可能にするか(デフォルトはtrue
)。deletable
(boolean): エッジを削除可能にするか(デフォルトはtrue
)。className
(string): エッジパスに適用するCSSクラス名。style
(object): エッジパスに直接適用するインラインCSSスタイルオブジェクト。markerStart
(string | EdgeMarkerType): 始点側の矢印/マーカー。組み込みタイプ(MarkerType.Arrow
,MarkerType.ArrowClosed
)またはカスタムマーカーのURL(例:'url(#my-custom-marker)'
)。markerEnd
(string | EdgeMarkerType): 終点側の矢印/マーカー。data
(object): エッジに関連付けたい任意のデータ。zIndex
(number): エッジの描画順序を制御。高い値ほど手前に描画されます。
例:異なるタイプのエッジ定義
javascript
const myEdges = [
{ id: 'a-b', source: 'A', target: 'B', type: 'step', label: 'ステップタイプ', animated: true },
{ id: 'b-c', source: 'B', target: 'C', type: 'smoothstep', label: 'スムーズステップ', style: { stroke: '#f97316', strokeWidth: 2 } },
{ id: 'a-c', source: 'A', target: 'C', type: 'straight', label: '直線', selectable: false }, // 選択不可なエッジ
];
5.3. ハンドル (Handles)
ハンドルはノード上の接続ポイントです。エッジは一方のノードのソースハンドルともう一方のノードのターゲットハンドルを結びます。
- ソースハンドル (Source Handle): エッジの始点となるポイント。
- ターゲットハンドル (Target Handle): エッジの終点となるポイント。
デフォルトのノードタイプ ('default'
, 'input'
, 'output'
) には、特定の場所にハンドルが組み込まれています。カスタムノードを作成する際は、Handle
コンポーネントを使って明示的にハンドルを配置します(後述)。
ノード定義の sourcePosition
および targetPosition
プロパティは、これらの組み込みハンドルのデフォルト位置を上書きするために使用できます。
5.4. ビューポート (Viewport) と ペイン (Pane)
- ペイン (Pane): フロー図が描画される背景領域です。ノードはこのペインの上に配置されます。
- ビューポート (Viewport): ユーザーが見ているペインの一部です。ズームレベルとペイン内の位置(パン)によって定義されます。React Flowは、ユーザーのインタラクション(ズーム、パン、ノードのドラッグ)に応じてビューポートの状態を管理します。
ReactFlow
コンポーネントは、このペインとビューポートを提供し、その中でノードやエッジを描画します。
6. ノードとエッジの操作 (状態の更新)
React Flowは、渡された nodes
および edges
配列の状態を元にUIを描画します。ユーザーがノードをドラッグしたり、新しいエッジを作成したりすると、React Flowは onNodesChange
や onEdgesChange
、onConnect
といったハンドラを呼び出し、どのような変更が発生したか を通知します。
開発者は、これらのハンドラ内で通知された変更を自身のノード・エッジの状態に適用する必要があります。useNodesState
と useEdgesState
フックが返す onNodesChange
および onEdgesChange
ハンドラは、まさにこの「React Flowからの変更通知を受け取り、それを適用して新しい状態を返す」という処理を内部で行ってくれます。onConnect
ハンドラは、新しいエッジが作成された際に、そのエッジを状態に追加するために開発者自身が実装します(前述の例のように addEdge
ヘルパーを使うのが一般的です)。
6.1. プログラムによるノード/エッジの追加・削除・更新
ユーザーインタラクションだけでなく、ボタンクリックなどのイベントに応じてプログラムからノードやエッジを追加・削除・更新したい場合もあります。これは、useNodesState
や useEdgesState
から返されるセッター関数(setNodes
, setEdges
)を使って、状態の配列を直接操作することで行います。
例:ノードを追加するボタン
“`jsx
// Flow コンポーネントの中に追加するボタンの例
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]);
// 新しいノードを追加する関数
const addNode = () => {
const newNode = {
id: node-${nodes.length + 1}
, // ユニークなIDを生成 (例: node-4)
position: {
x: Math.random() * 400 + 50, // ランダムな位置
y: Math.random() * 300 + 50,
},
data: { label: 新しいノード ${nodes.length + 1}
},
type: ‘default’,
};
// setNodes に新しいノードを追加した配列を渡す
setNodes((nds) => nds.concat(newNode));
// または setNodes([…nodes, newNode]); でも良いが、関数形式の方が安全
};
return (
);
};
“`
この例では、addNode
関数が新しいノードオブジェクトを作成し、setNodes
を呼び出して既存の nodes
配列の末尾に新しいノードを追加しています。setNodes
に関数を渡す形式 ((nds) => nds.concat(newNode)
) は、直前の状態に基づいて次の状態を計算する場合に推奨されるReactのパターンです。
同様に、エッジを追加したい場合は setEdges
を、ノードやエッジを削除したい場合は setNodes
や setEdges
を呼び出す際に、対象の要素を取り除いた新しい配列を渡します。
例:IDを指定してノードを削除
“`javascript
const deleteNode = (nodeId) => {
setNodes((nds) => nds.filter(node => node.id !== nodeId));
// 削除されたノードに関連するエッジも削除する必要があることが多い
setEdges((eds) => eds.filter(edge => edge.source !== nodeId && edge.target !== nodeId));
};
// 例: 特定のノードがクリックされたときに削除
const onNodeClick = (event, node) => {
console.log(‘node click’, node);
// もし Shift キーが押されていたら削除する例
if (event.shiftKey) {
deleteNode(node.id);
}
};
// ReactFlow コンポーネントに onNodeClick ハンドラを追加
<ReactFlow
// …他の props
onNodeClick={onNodeClick}
{/ … /}
“`
このように、React Flowの状態はReactの通常の状態管理のルールに従います。状態(nodes
, edges
配列)を変更することで、React FlowはUIを自動的に更新します。
6.2. コールバックハンドラ
ReactFlow
コンポーネントは、ユーザーの様々なインタラクションに応答するための多数のコールバックプロパティを提供します。これらは必須ではありませんが、フロー図にインタラクティブな機能を追加する際に非常に役立ちます。
主なコールバックハンドラ:
onNodesChange
(NodeChange[] => void): ノードの状態が変更されたときに呼び出されます(位置変更、選択状態変更など)。useNodesState
から返されるハンドラを渡すのが最も一般的です。onEdgesChange
(EdgeChange[] => void): エッジの状態が変更されたときに呼び出されます(選択状態変更など)。useEdgesState
から返されるハンドラを渡すのが最も一般的です。onConnect
(Connection => void): ユーザーがドラッグによってノード間を接続し、新しいエッジが作成されたときに呼び出されます。onInit
(ReactFlowInstance => void): React Flowコンポーネントが完全にマウントされ、インスタンスが準備できたときに呼び出されます。このインスタンスを使って、後からプログラムで操作(例:fitView
,zoomTo
) を行うことができます(後述のuseReactFlow
も参照)。onMoveStart
(MouseEvent | TouchEvent, Viewport => void): ユーザーがペインまたはノードのドラッグを開始したときに呼び出されます。onMove
(MouseEvent | TouchEvent, Viewport => void): ユーザーがペインまたはノードをドラッグ中に呼び出されます。onMoveEnd
(MouseEvent | TouchEvent, Viewport => void): ユーザーがペインまたはノードのドラッグを終了したときに呼び出されます。onPaneReady
(ReactFlowInstance => void):onInit
とほぼ同じですが、特に描画が完了した後に何かをしたい場合に便利です。onPaneClick
(MouseEvent | TouchEvent => void): ペイン(背景)がクリックされたときに呼び出されます。onPaneContextMenu
(MouseEvent | TouchEvent => void): ペインが右クリックされたときに呼び出されます。onPaneMouseMove
(MouseEvent | TouchEvent => void): ペイン上でマウスが移動したときに呼び出されます。onNodeClick
(MouseEvent | TouchEvent, Node => void): ノードがクリックされたときに呼び出されます。onNodeDoubleClick
(MouseEvent | TouchEvent, Node => void): ノードがダブルクリックされたときに呼び出されます。onNodeMouseEnter
(MouseEvent | TouchEvent, Node => void): マウスカーソルがノード上に入ったときに呼び出されます。onNodeMouseMove
(MouseEvent | TouchEvent, Node => void): マウスカーソルがノード上で移動したときに呼び出されます。onNodeMouseLeave
(MouseEvent | TouchEvent, Node => void): マウスカーソルがノード上から離れたときに呼び出されます。onNodeContextMenu
(MouseEvent | TouchEvent, Node => void): ノードが右クリックされたときに呼び出されます。onEdgeClick
(MouseEvent | TouchEvent, Edge => void): エッジがクリックされたときに呼び出されます。onEdgeDoubleClick
(MouseEvent | TouchEvent, Edge => void): エッジがダブルクリックされたときに呼び出されます。onEdgeMouseEnter
(MouseEvent | TouchEvent, Edge => void): マウスカーソルがエッジ上に入ったときに呼び出されます。onEdgeMouseMove
(MouseEvent | TouchEvent, Edge => void): マウスカーソルがエッジ上で移動したときに呼び出されます。onEdgeMouseLeave
(MouseEvent | TouchEvent, Edge => void): マウスカーソルがエッジ上から離れたときに呼び出されます。onEdgeContextMenu
(MouseEvent | TouchEvent, Edge => void): エッジが右クリックされたときに呼び出されます。onNodesDelete
(Node[] => void): 選択されたノードが削除されたときに呼び出されます。onEdgesDelete
(Edge[] => void): 選択されたエッジが削除されたときに呼び出されます。onSelectionChange
({ nodes: Node[], edges: Edge[] } => void): 選択されているノードやエッジのセットが変更されたときに呼び出されます。
これらのハンドラは、アプリケーションがユーザーの操作に反応するために使用されます。例えば、ノードをクリックしたら詳細情報を表示するサイドバーを開いたり、エッジをダブルクリックしたらそのエッジのプロパティを編集するモーダルを表示したりといった機能は、これらのハンドラを使って実装します。
7. React Flow フックの詳細
React Flowは、関数コンポーネントで簡単に状態管理やインスタンス操作を行うための様々なカスタムフックを提供しています。既に useNodesState
, useEdgesState
, useOnConnect
を見ましたが、他にも重要なフックがあります。
7.1. useNodesState
と useEdgesState
これらのフックは、React Flowの状態管理の中核を担います。
“`jsx
import { useNodesState, useEdgesState } from ‘reactflow’;
const MyFlow = ({ initialNodes, initialEdges }) => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
// … onConnect ハンドラなどの定義 …
return (
);
};
“`
useNodesState(initialNodes)
:- 第1引数: ノードの初期状態となる配列。
- 返り値:
[nodes, setNodes, onNodesChange]
の配列。nodes
: 現在のノード配列。setNodes
: ノードの状態を更新するためのセッター関数(ReactのuseState
と同じ)。onNodesChange
: React Flowからノードの変更通知(ドラッグ、選択など)を受け取り、setNodes
を内部で呼び出して状態を更新するハンドラ関数。これをReactFlow
コンポーネントのonNodesChange
プロパティに渡す必要があります。
useEdgesState(initialEdges)
:- 第1引数: エッジの初期状態となる配列。
- 返り値:
[edges, setEdges, onEdgesChange]
の配列。edges
: 現在のエッジ配列。setEdges
: エッジの状態を更新するためのセッター関数。onEdgesChange
: React Flowからエッジの変更通知(選択など)を受け取り、setEdges
を内部で呼び出して状態を更新するハンドラ関数。これをReactFlow
コンポーネントのonEdgesChange
プロパティに渡す必要があります。
これらのフックを使うことで、React Flowの状態管理ボイラープレートコードを大幅に削減できます。ノードのドラッグや選択などのユーザー操作は onNodesChange
/onEdgesChange
が自動的に処理し、プログラムからの操作(追加、削除、手動更新)は setNodes
/setEdges
を使って行います。
7.2. useOnConnect
新しいエッジが作成されたときに呼び出されるハンドラを定義するためのフックです。
“`jsx
import { ReactFlow, addEdge, useNodesState, useEdgesState, useOnConnect } from ‘reactflow’;
import { useCallback } from ‘react’; // useCallback はパフォーマンスのために推奨
const MyFlow = ({ initialNodes, initialEdges }) => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
// useOnConnect: 新しいエッジが接続されたときに実行する副作用を定義
// 第一引数: connection オブジェクトを受け取るコールバック関数
// 第二引数: 依存配列 ([setEdges] が必要)
useOnConnect(useCallback((connection) => {
setEdges((eds) => addEdge(connection, eds));
}, [setEdges])); // setEdges が変更されたらコールバックを再生成
return (
{/ … /}
);
};
“`
useOnConnect
は、ReactFlow
コンポーネントの onConnect
プロパティの代替または補完として使用できます。onConnect
プロパティが単に接続イベントを受け取るのに対し、useOnConnect
はそのイベントに対する副作用(この場合は状態の更新)を宣言的に記述するのに便利です。内部的には、ReactFlow
のコンテキスト経由で接続イベントを購読しています。多くの場合、どちらか一方を使えば十分です。useOnConnect
を使うと、React Flowコンポーネント自体ではなく、コンポーネントツリー内のどこからでも接続イベントを処理できる利点があります。
7.3. useReactFlow
React Flowインスタンスへのアクセスを提供するフックです。このインスタンスを介して、プログラム的にビューポートを操作したり、ノードやエッジの情報を取得したりできます。
“`jsx
import { ReactFlow, useReactFlow } from ‘reactflow’;
import { useCallback } from ‘react’;
const MyFlow = ({ initialNodes, initialEdges }) => {
// useReactFlow を呼び出してインスタンスを取得
const instance = useReactFlow();
// ビューポートを特定の位置にフィットさせる関数
const fitViewHandler = useCallback(() => {
instance.fitView(); // fitView メソッドを呼び出す
}, [instance]);
// 現在のノードとエッジの情報をログ出力する関数
const logElements = useCallback(() => {
console.log(‘Nodes:’, instance.getNodes()); // 現在のノード配列を取得
console.log(‘Edges:’, instance.getEdges()); // 現在のエッジ配列を取得
console.log(‘Viewport:’, instance.getViewport()); // 現在のビューポート情報を取得
}, [instance]);
return (
>
{/ … Controls, MiniMap, Background … /}
);
};
// useReactFlow は ReactFlowProvider の内側でしか使えないため、App コンポーネントでラップが必要
const App = () => {
return (
);
};
“`
useReactFlow
は、ReactFlowProvider
のコンテキストを使用するため、ReactFlowProvider
でラップされたコンポーネントツリーのどこでも呼び出すことができます。これにより、ReactFlow
コンポーネント自身だけでなく、別の子コンポーネントからでもフロー図を操作できるようになります。
useReactFlow
が返すインスタンスの主なメソッド:
getNodes()
: 現在のノード配列を取得します。getEdges()
: 現在のエッジ配列を取得します。getViewport()
: 現在のビューポート情報{ x, y, zoom }
を取得します。setViewport(viewport, options)
: ビューポートを指定した状態に設定します。オプションでアニメーションなども指定できます。zoomIn(options)
: ズームインします。zoomOut(options)
: ズームアウトします。zoomTo(zoomLevel, options)
: 指定したズームレベルに設定します。fitView(options)
: 全ての要素または指定した要素がビューポートに収まるように調整します。screenToFlowPosition(screenPosition, options)
: 画面座標をフロー座標(ペイン上の位置)に変換します。flowToScreenPosition(flowPosition)
: フロー座標を画面座標に変換します。fitBounds(bounds, options)
: 指定した矩形領域がビューポートに収まるように調整します。toObject()
: フロー図の状態(ノード、エッジ、ビューポート)をオブジェクトとして取得します。これはフローの状態を保存するのに便利です。deleteElements({ nodes, edges })
: 指定したノードとエッジを削除します。
7.4. useStore
React Flowの内部状態(ノード、エッジ、ビューポート、選択状態など)に低レベルでアクセスするためのフックです。特定の情報(例: 現在のズームレベルだけ)を効率的に取得したい場合に使用できます。
“`jsx
import { useStore } from ‘reactflow’;
const ZoomLevelDisplay = () => {
// useStore にセレクター関数を渡すことで、必要な情報だけを購読できる
// ここではビューポートの zoom プロパティだけを取得
const zoomLevel = useStore((state) => state.transform[2]);
return (
);
};
const MyFlow = ({ initialNodes, initialEdges }) => {
// … nodes, edges, change handlers …
return (
{/ ZoomLevelDisplay を ReactFlow の子として配置 /}
{/ … Controls, MiniMap, Background … /}
);
};
// ReactFlowProvider の内側である必要がある
const App = () => {
return (
);
};
“`
useStore
は高度なユースケース向けで、通常は useNodesState
, useEdgesState
, useReactFlow
で事足ります。しかし、特定のステートの変更にのみ応答したい場合などに、パフォーマンス上の利点があります。セレクター関数は、React Flowの内部Reduxストアの state
オブジェクトを受け取り、必要な値を返すように記述します。
7.5. その他のフック
useUpdateNodeInternals()
: カスタムノードのサイズが変更されたり、ハンドルの位置が動的に変わったりした場合に、React Flowにそのノードの内部状態(ハンドルの位置など)を再計算させる必要がある場合に呼び出します。useNodesInitialized()
: ノードがビューポート内で初期化され、正しいサイズや位置が確定した後に何か処理を行いたい場合(例: 初期レイアウト計算)に使用できます。useKeyPress(keyCode)
: 特定のキーが押されたかどうかを検出するユーティリティフック。useNodes()
/useEdges()
: 現在のノード/エッジの配列を取得するフック (useReactFlow().getNodes()
/getEdges()
と似ていますが、フックとして提供されます)。useViewport()
: 現在のビューポート情報{ x, y, zoom }
を取得するフック。
これらのフックを組み合わせることで、非常に複雑でインタラクティブなフロー図アプリケーションを構築することが可能になります。
8. ヘルパーコンポーネント
最初の例でも使用したように、React Flowはフロー図をより使いやすくするための便利なUIコンポーネントをいくつか提供しています。これらは ReactFlow
コンポーネントの子要素として配置します。
8.1. Controls
ズームイン、ズームアウト、フィットビュー、ビューポートロックなどのボタンを提供するコンポーネントです。
“`jsx
import { ReactFlow, Controls } from ‘reactflow’;
const MyFlow = () => {
// … nodes, edges, handlers …
return (
{/ … MiniMap, Background … /}
);
};
“`
主なProps:
showZoom
(boolean): ズームボタンを表示するか。showFitView
(boolean): フィットビューボタンを表示するか。showInteractive
(boolean): インタラクションロックボタンを表示するか。position
(‘top-left’ | ‘top-right’ | ‘bottom-left’ | ‘bottom-right’): コントロールボタンを表示する位置。onZoomIn
,onZoomOut
,onFitView
,onInteractiveChange
: ボタンがクリックされたときのカスタムコールバック。
8.2. MiniMap
フロー図全体の鳥瞰図(ミニマップ)を表示するコンポーネントです。大規模なフロー図で現在表示されている領域を確認したり、簡単に別の領域に移動したりするのに便利です。
“`jsx
import { ReactFlow, MiniMap } from ‘reactflow’;
const MyFlow = () => {
// … nodes, edges, handlers …
return (
{/ … Controls … /}
{/ … Background … /}
);
};
“`
主なProps:
position
(‘top-left’ | ‘top-right’ | ‘bottom-left’ | ‘bottom-right’): ミニマップを表示する位置。nodeStrokeColor
((node: Node) => string): 各ノードの枠線の色を決定する関数、または固定色。nodeColor
((node: Node) => string): 各ノードの塗りつぶしの色を決定する関数、または固定色。nodeClassName
((node: Node) => string): 各ノードに適用するCSSクラス名を決定する関数。nodeBorderRadius
(number): ミニマップ上のノードの角丸の半径。maskColor
(string): ミニマップ上で現在表示されていない領域の色。maskStrokeColor
(string): マスクの枠線の色。maskStrokeWidth
(number): マスクの枠線の太さ。onNodeClick
((event: MouseEvent, node: Node) => void): ミニマップ上のノードがクリックされたときのコールバック。className
,style
: コンテナ要素に適用するCSSクラス名、スタイル。
8.3. Background
フロー図の背景にグリッドやドットなどのパターンを表示するコンポーネントです。ノードの位置を揃えたり、視覚的なガイドとして役立ちます。
“`jsx
import { ReactFlow, Background } from ‘reactflow’;
const MyFlow = () => {
// … nodes, edges, handlers …
return (
{/ … Controls, MiniMap … /}
{/ or /}
{/
);
};
“`
主なProps:
variant
(‘dots’ | ‘lines’ | ‘cross’): 表示するパターンの種類。gap
(number | number[]): ドットや線の間隔。数値一つなら縦横共通、配列なら[x, y]
。size
(number): ドットや線のサイズ。color
(string): パターンの色。lineWidth
(number): 線の太さ (variant="lines"
または"cross"
の場合)。className
,style
: コンテナ要素に適用するCSSクラス名、スタイル。
これらのヘルパーコンポーネントを組み合わせることで、ユーザーフレンドリーなフロー図ビューを手軽に構築できます。
9. カスタムノードとカスタムエッジ
React Flowの強力な機能の一つは、デフォルトの見た目や挙動にとらわれず、完全に独自のノードやエッジコンポーネントを作成できることです。これにより、アプリケーションの要件に合わせたリッチな表現が可能になります。
9.1. カスタムノードの作成
カスタムノードを作成するには、まず通常のReactコンポーネントとしてノードの見た目を定義します。このコンポーネントは、React Flowから以下のプロパティを受け取ります。
id
(string): ノードのID。data
(object): ノード定義で渡されたdata
オブジェクト。type
(string): ノードのタイプ。selected
(boolean): ノードが選択されているか。isConnectable
(boolean): このノードがエッジ接続可能か (node.connectable
やReactFlow
のnodesConnectable
プロパティで指定)。xPos
,yPos
(number): ペイン内のノードの左上隅のフロー座標。dragging
(boolean): ノードがドラッグされているか。targetPosition
(Position): このノードがエッジの終点として接続される場合の、エッジの接続位置ヒント。sourcePosition
(Position): このノードがエッジの始点として接続される場合の、エッジの接続位置ヒント。
さらに、カスタムノード内でハンドル(エッジの接続ポイント)を配置するには、React Flowが提供する Handle
コンポーネントを使用します。
Handle
コンポーネントの主なプロパティ:
type
(‘source’ | ‘target’): ハンドルの種類。エッジの始点になるか終点になるか。position
(Position): ハンドルの位置('top'
,'right'
,'bottom'
,'left'
)。id
(string): オプション。ノード上に複数の同じ種類のハンドルがある場合に、それらを区別するためのユニークなID。エッジ定義のsourceHandle
やtargetHandle
と紐づけます。isConnectable
(boolean | number | Connectable): このハンドルが接続可能か。false
で接続不可、true
で接続可能。数値を渡すと、同時に接続できるエッジの最大数を制限できます。関数を渡してより複雑な接続ルールを設定することも可能です。onConnect
((connection: Connection) => void): このハンドルから新しいエッジが作成されたときに呼び出されるコールバック。isValidConnection
((connection: Connection) => boolean): このハンドルから提案された接続が有効かどうかを判定する関数。className
,style
: ハンドル要素に適用するCSSクラス名、スタイル。
カスタムノードの実装例:
src/components/CustomNode.jsx
を作成します。
“`jsx
import React from ‘react’;
import { Handle, Position } from ‘reactflow’;
import ‘./CustomNode.css’; // カスタムノード用のCSS
const CustomNode = ({ data, isConnectable }) => {
return (
{data.heading}
{data.buttonText && }
{/ ソースハンドル (通常はノードの下側か右側) /}
);
};
export default CustomNode;
“`
src/components/CustomNode.css
:
“`css
.custom-node {
border: 1px solid #eee;
padding: 15px;
border-radius: 5px;
background: white;
width: 150px; / 例: 固定幅 /
min-height: 50px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
font-size: 12px;
}
.custom-node .react-flow__handle {
/ ハンドルのデフォルトスタイルを調整 /
width: 10px;
height: 10px;
border-radius: 50%;
background: #555;
}
/ ハンドルの位置に応じた細かい調整が必要になる場合がある /
.custom-node .react-flow__handle.react-flow__handle-top {
top: -5px; / 上側ハンドルの微調整 /
}
.custom-node .react-flow__handle.react-flow__handle-bottom {
bottom: -5px; / 下側ハンドルの微調整 /
}
.custom-node .react-flow__handle.react-flow__handle-right {
right: -5px; / 右側ハンドルの微調整 /
}
.custom-node strong {
margin-bottom: 5px;
}
.custom-node button {
margin-top: 10px;
padding: 5px 10px;
font-size: 10px;
cursor: pointer;
}
“`
React Flowでのカスタムノードの利用:
カスタムノードを使用するには、ReactFlow
コンポーネントの nodeTypes
プロパティに、タイプ名とコンポーネントのマッピングをオブジェクトとして渡します。
src/App.jsx
(または Flow.jsx
):
“`jsx
import ReactFlow, { ReactFlowProvider, addEdge, useNodesState, useEdgesState, Controls, MiniMap, Background, Position } from ‘reactflow’;
import ‘reactflow/dist/style.css’;
import { useCallback } from ‘react’;
// カスタムノードコンポーネントをインポート
import CustomNode from ‘./components/CustomNode’;
// カスタムノードのマッピングオブジェクトを作成
const nodeTypes = {
custom: CustomNode, // ‘custom’ というタイプ名で CustomNode を使用する
// 他のカスタムノードがあればここに追加
};
// 初期ノードデータにカスタムノードを含める
const initialNodes = [
// … デフォルトノードなど …
{
id: ‘custom-1’,
position: { x: 500, y: 150 },
type: ‘custom’, // カスタムノードタイプを指定
data: {
heading: ‘カスタムノード’,
text: ‘これはカスタマイズされたノードです。’,
buttonText: ‘クリック!’,
onButtonClick: () => alert(‘ボタンがクリックされました!’),
},
},
{
id: ‘custom-2’,
position: { x: 500, y: 300 },
type: ‘custom’, // もう一つのカスタムノード
data: {
heading: ‘別のカスタムノード’,
text: ‘別のデータを持っています。’,
},
},
];
const initialEdges = [
// … 既存のエッジ …
// カスタムノードのハンドルIDを指定して接続
{ id: ‘e-c1-c2’, source: ‘custom-1’, sourceHandle: ‘a’, target: ‘custom-2’, type: ‘step’, label: ‘ハンドルaから’ },
{ id: ‘e-c1-c2-b’, source: ‘custom-1’, sourceHandle: ‘b’, target: ‘custom-2’, type: ‘straight’, label: ‘ハンドルbから’ },
];
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]);
return (
);
};
const App = () => {
return (
);
};
export default App;
“`
これで、定義したカスタムノードがフロー図に表示されるようになります。Handle
コンポーネントを使って配置した位置に接続ポイントが現れ、そこからドラッグして他のノードとエッジを接続できるようになります。sourceHandle
または targetHandle
をエッジ定義で指定することで、特定のハンドルからエッジを開始・終了させることも可能です。
9.2. カスタムエッジの作成
カスタムエッジもカスタムノードと同様に、Reactコンポーネントとして作成します。カスタムエッジコンポーネントはSVG要素としてレンダリングされるため、SVGのパスを描画する必要があります。React Flowは、エッジの始点と終点の座標、ハンドル情報などをプロパティとして渡してくれます。
カスタムエッジコンポーネントが受け取る主なプロパティ:
id
(string): エッジのID。sourceX
,sourceY
(number): 始点ハンドルの画面座標(ビューポート考慮済み)。targetX
,targetY
(number): 終点ハンドルの画面座標(ビューポート考慮済み)。sourcePosition
(Position): 始点ハンドルのノード上での位置。targetPosition
(Position): 終点ハンドルのノード上での位置。source
(string): 始点ノードのID。target
(string): 終点ノードのID。sourceHandleId
(string): 始点ハンドルのID。targetHandleId
(string): 終点ハンドルのID。markerEnd
(string): 終点側のマーカー(矢印など)のURL(例:'url(#reactflow__arrow)'
)。markerStart
(string): 始点側のマーカーのURL。data
(object): エッジ定義で渡されたdata
オブジェクト。selected
(boolean): エッジが選択されているか。animated
(boolean): エッジがアニメーション表示されるか。label
(string | ReactNode): エッジのラベル。labelX
,labelY
(number): ラベルを表示すべき中央付近の座標。labelStyle
(object): ラベルのスタイル。labelShowBg
(boolean): ラベルの背景を表示するか。labelBgStyle
(object): ラベルの背景スタイル。labelBgPadding
([number, number]): ラベル背景のパディング。labelBgBorderRadius
(number): ラベル背景の角丸。style
(object): エッジパスに適用するインラインCSSスタイル。
React Flowは、始点と終点の座標、ハンドルの位置、エッジタイプなどに基づいて、エッジのパスデータを計算するためのユーティリティ関数を提供しています。最もよく使われるのは getBezierPath
や getSmoothStepPath
、getStepPath
です。
カスタムエッジの実装例:
src/components/CustomEdge.jsx
を作成します。この例では、React Flowのユーティリティを使ってパスを計算し、そのパス上にラベルを表示します。
“`jsx
import React from ‘react’;
import { BaseEdge, EdgeLabelRenderer, getBezierPath } from ‘reactflow’;
// カスタムエッジコンポーネント
const CustomEdge = ({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style = {}, // スタイルを受け取る
label, // ラベルを受け取る
}) => {
// getBezierPath: 始点、終点、ハンドルの位置からベジエ曲線のパスデータを計算
// [edgePath, labelX, labelY] を返す
const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
return (
<>
{/ BaseEdge: デフォルトのエッジパスを描画するコンポーネント。カスタムパスと組み合わせて使うことが多い /}
{/ d: SVGパスデータ, style: スタイルを適用 /}
{/* EdgeLabelRenderer: エッジパス上にHTML要素のラベルを描画するためのヘルパー */}
<EdgeLabelRenderer>
{/* ラベル要素を、計算されたラベル位置 (labelX, labelY) に絶対位置で配置 */}
<div
style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
pointerEvents: 'all', // ラベルをクリック可能にする
backgroundColor: 'rgba(255, 255, 255, 0.7)',
padding: '2px 5px',
borderRadius: '3px',
fontSize: 10,
fontWeight: 700,
}}
className="nodrag nopan" // ドラッグやパンの対象外にするクラス (React Flowのクラス)
>
{label} {/* 受け取ったラベルを表示 */}
</div>
</EdgeLabelRenderer>
);
};
export default CustomEdge;
“`
React Flowでのカスタムエッジの利用:
カスタムエッジを使用するには、ReactFlow
コンポーネントの edgeTypes
プロパティに、タイプ名とコンポーネントのマッピングをオブジェクトとして渡します。
src/App.jsx
(または Flow.jsx
):
“`jsx
import ReactFlow, { ReactFlowProvider, addEdge, useNodesState, useEdgesState, Controls, MiniMap, Background } from ‘reactflow’;
import ‘reactflow/dist/style.css’;
import { useCallback } from ‘react’;
// カスタムノードとカスタムエッジをインポート
import CustomNode from ‘./components/CustomNode’;
import CustomEdge from ‘./components/CustomEdge’; // カスタムエッジ
// カスタムノードとエッジのマッピングオブジェクト
const nodeTypes = { custom: CustomNode };
const edgeTypes = { custom: CustomEdge }; // ‘custom’ というタイプ名で CustomEdge を使用する
// 初期ノードデータ (前と同じ)
const initialNodes = [ / … / ];
// 初期エッジデータにカスタムエッジを含める
const initialEdges = [
// … 既存のエッジ …
{
id: ‘e-c1-c2-custom’, // ユニークなID
source: ‘custom-1’, // 始点ノードID
sourceHandle: ‘a’, // 始点ハンドルID (カスタムノードのもの)
target: ‘custom-2’, // 終点ノードID
type: ‘custom’, // カスタムエッジタイプを指定
label: ‘カスタムエッジ’, // エッジラベル
style: { strokeWidth: 2, stroke: ‘#a16207’ }, // カスタムスタイルを渡す
},
// もう一つカスタムエッジ
{
id: ‘e-1-custom1’,
source: ‘1’, // デフォルトノードから
target: ‘custom-1’, // カスタムノードへ
type: ‘custom’,
label: ‘デフォルト->カスタム’,
animated: true, // アニメーションも可能
},
];
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]);
return (
);
};
const App = () => {
return (
);
};
export default App;
“`
これで、定義したカスタムエッジがフロー図に表示されるようになります。CustomEdge
コンポーネント内で getBezierPath
を使用しているので、エッジはベジエ曲線として描画され、指定したラベルがパスの中央付近に表示されます。BaseEdge
を使うと、エッジの選択状態に応じたハイライト表示などもReact Flowが自動的に処理してくれます。
10. その他の機能(簡易説明)
この詳細な記事では基本的な機能に焦点を当てましたが、React Flowには他にも多くの機能があります。ここではそれらを簡単に紹介します。
- Layouting (レイアウト): React Flow自体には自動レイアウト機能はありませんが、Dagre, ELKjs, Cola.jsなどの外部ライブラリと組み合わせることで、ノードを自動的に整列させることができます。
useReactFlow
のインスタンスやノード/エッジの状態操作を使ってこれらのライブラリの計算結果を反映させます。 - Saving and Restoring (保存と復元):
useReactFlow().toObject()
メソッドを使うと、現在のノード、エッジ、ビューポートの状態をJSONオブジェクトとして取得できます。これを保存し、後で初期データとしてuseNodesState
/useEdgesState
に渡すことでフロー図の状態を復元できます。 - Drag and Drop (DND): 外部の要素(例: パレット上のノードタイプアイコン)をフロー図にドラッグ&ドロップして新しいノードを作成する機能は、Reactの標準的なDND APIと
useReactFlow().screenToFlowPosition
メソッドを組み合わせて実装します。 - Validation (検証):
ReactFlow
コンポーネントのisValidConnection
プロパティに関数を渡したり、カスタムハンドルのisValidConnection
プロパティを使ったりすることで、エッジ接続の際に特定のルール(例: 同じタイプのノード間は接続できない、特定のノードには1つしかエッジを接続できないなど)を適用できます。 - Accessibility (アクセシビリティ): React Flowは基本的なキーボード操作やWAI-ARIA属性をサポートしており、アクセシビリティにも配慮されています。カスタムノードを作成する際も、適切なARIA属性などを追加することが推奨されます。
- Multi-Flows (複数フロー):
ReactFlowProvider
を使用することで、一つのアプリケーション内に複数の独立したReact Flowインスタンスを持つことができます。
これらの機能は、より高度なアプリケーションを構築する際に役立ちます。必要に応じて公式ドキュメントを参照してください。
11. まとめと次のステップ
この記事では、React Flowのインストールから、ノードとエッジの基本、状態管理のためのフック、インタラクションの実現、そしてカスタムノード・エッジの作成まで、基本的な機能を網羅的に解説しました。
React Flowは宣言的なアプローチ、豊富なカスタマイズ性、Reactとの優れた統合により、Webアプリケーションでフロー図やグラフを表示する際に非常に強力なツールとなります。useNodesState
や useEdgesState
といったフックを使うことで、ノードやエッジの状態管理をReactの仕組みに則って簡単に行えます。また、カスタムコンポーネント機能を使えば、どのような見た目や振る舞いのノード・エッジも実現可能です。
次のステップ:
- 練習: 記事で紹介したコード例を実際に自分のPCで動かしてみましょう。値を変更したり、新しいノードやエッジを追加したり、削除機能を実装したり、様々なタイプのエッジを試したりしてみてください。
- カスタムノード/エッジの応用: もっと複雑なデータを持つカスタムノードを作成したり、エッジの色をデータの値によって変えたりするカスタムエッジを作成してみましょう。
- インタラクションの応用:
onNodeClick
やonEdgeClick
ハンドラを使って、要素がクリックされたときに情報を表示する機能などを実装してみましょう。 - 公式ドキュメント: React Flowの公式ドキュメント (https://reactflow.dev/docs) は非常に充実しています。この記事で紹介しきれなかったより多くの機能や詳細な情報、高度な使い方(レイアウト、DNDなど)について学ぶことができます。
- 例を見る: 公式サイトには多くのデモや例があります。それらのソースコードを見て、どのように実装されているかを学ぶのも良い方法です。
React Flowは、視覚化の可能性を大きく広げるライブラリです。ぜひ様々なアイデアを形にするために活用してください。
これで、「ゼロからわかるReact Flow:インストールから基本機能まで」の詳細な記事は終わりです。この情報が、あなたがReact Flowを始めるための一助となれば幸いです。