react-flow-architecture
React Flowを使ったノードベースUIの構築に関するアーキテクチャ的なガイダンスを提供します。フローベースのアプリケーション設計、状態管理の意思決定、インテグレーションパターンの検討、またはReact Flowがユースケースに適しているかどうかの評価を行う際に活用してください。
description の原文を見る
Architectural guidance for building node-based UIs with React Flow. Use when designing flow-based applications, making decisions about state management, integration patterns, or evaluating whether React Flow fits a use case.
SKILL.md 本文
React Flow アーキテクチャ
React Flow の使用時期
適している場合
- ビジュアルプログラミングインターフェース
- ワークフロービルダーと自動化ツール
- ダイアグラムエディタ (フローチャート、組織図)
- データパイプラインの可視化
- マインドマップツール
- ノードベースのオーディオ/ビデオエディタ
- デシジョンツリービルダー
- ステートマシンデザイナー
代替案の検討
- シンプルな静的ダイアグラム (SVG または canvas を直接使用)
- 重いリアルタイムコラボレーション (カスタムシンク層が必要な場合がある)
- 3D ビジュアライゼーション (Three.js、react-three-fiber を使用)
- 10k 以上のノードのグラフ分析 (Sigma.js のような WebGL ベースのソリューションを使用)
デシジョンワークフロー (ゲート)
実装を開始またはスプリント計画を立てる前に、このシーケンスを実行します。使い捨てプロトタイプの場合のみスキップできます。
-
インタラクションを命名する — トップユーザーアクション (例: ドラッグ、接続、削除、グループ化) をリストアップします。合格: 各アクションが実装する具体的な React Flow コールバック (
onNodesChange、onConnectなど) にマッピングされる。 -
スケールを分類する — ピークノード数 (キャンバス表示またはドキュメント合計) を推定します。合格: あなたの範囲が Node Count Guidelines のいずれかの行に一致し、そのリストされた戦略 (例: その行が示唆する場合の
onlyRenderVisibleElements) を受け入れている。 -
状態を配置する — ローカルフック、外部ストア、または Redux/その他を選択します。合格: 1 文でパーシスタンス、アンドゥ、またはクロスサーフェース同期がどこに存在するか、または明示的に「まだ不要」と述べている。
-
代替案を再チェックする — ユースケースが Consider Alternatives に該当する場合、合格: 1 文で React Flow がまだ適している理由を説明するか、代わりに選択したリストされた代替案を述べている。
アーキテクチャパターン
パッケージ構造 (xyflow)
@xyflow/system (vanilla TypeScript)
├── Core algorithms (edge paths, bounds, viewport)
├── xypanzoom (d3-based pan/zoom)
├── xydrag, xyhandle, xyminimap, xyresizer
└── Shared types
@xyflow/react (depends on @xyflow/system)
├── React components and hooks
├── Zustand store for state management
└── Framework-specific integrations
@xyflow/svelte (depends on @xyflow/system)
└── Svelte components and stores
含意: コアロジックはフレームワークに依存しません。貢献またはデバッグ時に、問題が @xyflow/system かフレームワーク固有パッケージのどちらにあるかを確認してください。
状態管理のアプローチ
1. ローカル状態 (シンプルなアプリ)
// useNodesState/useEdgesState for prototyping
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
メリット: シンプル、ボイラープレートが少ない デメリット: 状態はコンポーネントツリーに限定される
2. 外部ストア (本番環境)
// Zustand store example
import { create } from 'zustand';
interface FlowStore {
nodes: Node[];
edges: Edge[];
setNodes: (nodes: Node[]) => void;
onNodesChange: OnNodesChange;
}
const useFlowStore = create<FlowStore>((set, get) => ({
nodes: initialNodes,
edges: initialEdges,
setNodes: (nodes) => set({ nodes }),
onNodesChange: (changes) => {
set({ nodes: applyNodeChanges(changes, get().nodes) });
},
}));
// In component
function Flow() {
const { nodes, edges, onNodesChange } = useFlowStore();
return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}
メリット: 状態はどこからでもアクセス可能、パーシスタンス/同期が簡単 デメリット: セットアップが多い、セレクタ最適化に注意が必要
3. Redux/その他の状態管理ライブラリ
// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();
const onNodesChange = useCallback((changes: NodeChange[]) => {
dispatch(nodesChanged(changes));
}, [dispatch]);
データフローアーキテクチャ
User Input → Change Event → Reducer/Handler → State Update → Re-render
↓
[Drag node] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
↓
[Connect] → onConnect → addEdge → setEdges → ReactFlow
↓
[Delete] → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow
サブフローパターン (ネストされたノード)
// Parent node containing child nodes
const nodes = [
{
id: 'group-1',
type: 'group',
position: { x: 0, y: 0 },
style: { width: 300, height: 200 },
},
{
id: 'child-1',
parentId: 'group-1', // Key: parent reference
extent: 'parent', // Key: constrain to parent
position: { x: 10, y: 30 }, // Relative to parent
data: { label: 'Child' },
},
];
検討事項:
- ドラッグを親に制限するには
extent: 'parent'を使用 - 親を自動展開するには
expandParent: trueを使用 - 親の z-index は子のレンダリング順序に影響
ビューポートの永続化
// Save viewport state
const { toObject, setViewport } = useReactFlow();
const handleSave = () => {
const flow = toObject();
// flow.nodes, flow.edges, flow.viewport
localStorage.setItem('flow', JSON.stringify(flow));
};
const handleRestore = () => {
const flow = JSON.parse(localStorage.getItem('flow'));
setNodes(flow.nodes);
setEdges(flow.edges);
setViewport(flow.viewport);
};
統合パターン
バックエンド/API との統合
// Load from API
useEffect(() => {
fetch('/api/flow')
.then(r => r.json())
.then(({ nodes, edges }) => {
setNodes(nodes);
setEdges(edges);
});
}, []);
// Debounced auto-save
const debouncedSave = useMemo(
() => debounce((nodes, edges) => {
fetch('/api/flow', {
method: 'POST',
body: JSON.stringify({ nodes, edges }),
});
}, 1000),
[]
);
useEffect(() => {
debouncedSave(nodes, edges);
}, [nodes, edges]);
レイアウトアルゴリズムとの統合
import dagre from 'dagre';
function getLayoutedElements(nodes: Node[], edges: Edge[]) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' });
g.setDefaultEdgeLabel(() => ({}));
nodes.forEach((node) => {
g.setNode(node.id, { width: 150, height: 50 });
});
edges.forEach((edge) => {
g.setEdge(edge.source, edge.target);
});
dagre.layout(g);
return {
nodes: nodes.map((node) => {
const pos = g.node(node.id);
return { ...node, position: { x: pos.x, y: pos.y } };
}),
edges,
};
}
パフォーマンススケーリング
ノード数ガイドライン
| ノード数 | 戦略 |
|---|---|
| < 100 | デフォルト設定 |
| 100-500 | onlyRenderVisibleElements を有効化 |
| 500-1000 | カスタムノードを簡素化、DOM 要素を削減 |
| > 1000 | 仮想化、WebGL 代替案の検討 |
最適化テクニック
<ReactFlow
// Only render nodes/edges in viewport
onlyRenderVisibleElements={true}
// Reduce node border radius (improves intersect calculations)
nodeExtent={[[-1000, -1000], [1000, 1000]]}
// Disable features not needed
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
/>
トレードオフ
制御 vs 非制御
| 制御 | 非制御 |
|---|---|
| ボイラープレートが多い | コードが少ない |
| 完全な状態制御 | 内部状態 |
| パーシスタンスが簡単 | toObject() が必要 |
| 複雑なアプリに最適 | プロトタイプに良い |
接続モード
| Strict (デフォルト) | Loose |
|---|---|
| Source → Target のみ | あらゆるハンドル → あらゆるハンドル |
| 予測可能な動作 | より柔軟 |
| データフロー向け | ダイアグラム向け |
<ReactFlow connectionMode={ConnectionMode.Loose} />
エッジレンダリング
| デフォルトエッジ | カスタムエッジ |
|---|---|
| 高速レンダリング | より多くの制御 |
| スタイリングに制限 | あらゆる SVG/HTML |
| シンプルなユースケース | 複雑なラベル |
ライセンス: 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
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。