react-flow-code-review について
React Flow を使ったアプリをレビューするスキルです。順序に沿った確認と根拠付きの指摘により、Provider の使い方、コンポーネントの安定性、パフォーマンス、TypeScript の実践を評価します。
主な機能
- 01レビュー範囲内の React Flow 利用箇所の特定
- 02フックの Provider 境界の確認
- 03カスタムノードとエッジの安定性評価
- 04描画と状態更新のパフォーマンス確認
- 05TypeScript の型と指摘根拠の確認
活用シーン
- 01マージ前の React Flow 機能レビュー
- 02Provider 外で呼び出されたフックの検出
- 03ノードやエッジの不要な再描画の特定
- 04React Flow コードの型の問題確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill react-flow-code-reviewインストール後、Claude Code で/react-flow-code-reviewを入力して読み込まれたか確認します。
詳細
React Flow のコードレビュー
React Flow のコードをレビューする際は、以下のゲートを順番に完了してください。各ステップには、次へ進む前に満たすべき客観的な合格条件があります。
レビューゲート(順番に実施)
-
フローコードを特定する — レビュー対象の範囲で
ReactFlow、ReactFlowProvider、useReactFlow、@xyflow/react、nodeTypes、edgeTypesを検索します。合格条件: ファイルパスの短い一覧を提示するか、検索後に対象範囲内に「該当なし」と明記します。 -
プロバイダーの境界 — 各
useReactFlow()(およびプロバイダーを必要とするその他のフック)について、コンポーネントツリーをたどり、囲んでいるReactFlowProviderがあることを確認するか、具体的な不一致を ファイル名:行番号 で記録します。 -
型の安定性とメモ化の対象 — カスタムノードまたはエッジの各コンポーネントについて、
memoと型付きの props(NodeProps<...>など)を使用しているかを記録します。<ReactFlow>に渡される各nodeTypes/edgeTypesの値について、安定した参照(モジュールスコープ、または依存配列を示せるuseMemo)であることを確認するか、不安定な再生成を ファイル名:行番号 で指摘します。 -
根拠を添えて報告する — 報告する各指摘について、ファイルパスと行番号(または最小限の引用スニペット)を記録します。合格条件: 重大または高重大度の問題について、その引用を添えずに記載していません。
-
チェックリストを完了する — パフォーマンスチェックリスト と よくある間違い を使用し、各項目を 満たしている、(理由を添えて)該当なし、または根拠を添えて 未解決 のいずれかにします。合格条件: 暗黙のまま曖昧になっている項目がありません。
重大なアンチパターン
1. コンポーネント内で nodeTypes/edgeTypes を定義する
問題: レンダリングのたびにすべてのノードが再マウントされます。
// BAD - recreates object every render
function Flow() {
const nodeTypes = { custom: CustomNode }; // WRONG
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - defined outside component
const nodeTypes = { custom: CustomNode };
function Flow() {
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - useMemo if dynamic
function Flow() {
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
return <ReactFlow nodeTypes={nodeTypes} />;
}
2. カスタムノード/エッジに memo() がない
問題: 親コンポーネントが更新されるたびに、カスタムコンポーネントが再レンダリングされます。
// BAD - no memoization
function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
}
// GOOD - wrapped in memo
import { memo } from 'react';
const CustomNode = memo(function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
});
3. useCallback を使わないインラインコールバック
問題: 新しい関数参照が作成され、メモ化が機能しなくなります。
// BAD - inline callback
<ReactFlow
onNodesChange={(changes) => setNodes(applyNodeChanges(changes, nodes))}
/>
// GOOD - memoized callback
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
<ReactFlow onNodesChange={onNodesChange} />
4. プロバイダーの外部で useReactFlow を使用する
// BAD - will throw error
function App() {
const { getNodes } = useReactFlow(); // ERROR: No provider
return <ReactFlow ... />;
}
// GOOD - wrap in provider
function FlowContent() {
const { getNodes } = useReactFlow(); // Works
return <ReactFlow ... />;
}
function App() {
return (
<ReactFlowProvider>
<FlowContent />
</ReactFlowProvider>
);
}
5. ノードデータに複雑なオブジェクトを格納する
問題: 参照の等価性チェックに失敗し、不要な更新が発生します。
// BAD - new object reference every time
setNodes(nodes.map(n => ({
...n,
data: { ...n.data, config: { nested: 'value' } } // New object each time
})));
// GOOD - use updateNodeData for targeted updates
const { updateNodeData } = useReactFlow();
updateNodeData(nodeId, { config: { nested: 'value' } });
パフォーマンスチェックリスト
ノードのレンダリング
- カスタムノードを
memo()でラップしている - nodeTypes をコンポーネントの外部で定義するか、メモ化している
- ノード内の負荷の高い計算に
useMemoを使用している - イベントハンドラーに
useCallbackを使用している
エッジのレンダリング
- カスタムエッジを
memo()でラップしている - edgeTypes をコンポーネントの外部で定義するか、メモ化している
- エッジのパス計算が重複していない
状態の更新
- setState の関数形式を使用している:
setNodes((nds) => ...) - 単一プロパティの更新で状態全体をスプレッドしていない
- データのみを変更する場合に
updateNodeDataを使用している - 複数のノード/エッジを追加する際に更新をまとめている
ビューポート
- レンダリングのたびに
fitView()を呼び出していない - 初回の表示調整にのみ
fitViewOptionsを使用している - アニメーションの時間が適切である(500ms 未満)
よくある間違い
コンテナーの高さが設定されていない
// BAD - no height, flow won't render
<ReactFlow nodes={nodes} edges={edges} />
// GOOD - explicit dimensions
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow nodes={nodes} edges={edges} />
</div>
CSS のインポートがない
// Required for default styles
import '@xyflow/react/dist/style.css';
インタラクティブ要素に nodrag を付け忘れている
// BAD - clicking button drags node
<button onClick={handleClick}>Click</button>
// GOOD - prevents drag
<button className="nodrag" onClick={handleClick}>Click</button>
位置定数を使用していない
// BAD - string literals
<Handle type="source" position="right" />
// GOOD - type-safe constants
import { Position } from '@xyflow/react';
<Handle type="source" position={Position.Right} />
ノード/エッジを直接変更している
// BAD - direct mutation
nodes[0].position = { x: 100, y: 100 };
setNodes(nodes);
// GOOD - immutable update
setNodes(nodes.map(n =>
n.id === '1' ? { ...n, position: { x: 100, y: 100 } } : n
));
TypeScript の問題
ジェネリック型が指定されていない
// BAD - loses type safety
const [nodes, setNodes] = useNodesState(initialNodes);
// GOOD - explicit types
type MyNode = Node<{ value: number }, 'custom'>;
const [nodes, setNodes] = useNodesState<MyNode>(initialNodes);
props の型が間違っている
// BAD - using wrong type
function CustomNode(props: any) { ... }
// GOOD - correct props type
function CustomNode(props: NodeProps<MyNode>) { ... }
レビューの質問
- すべてのカスタムコンポーネントがメモ化されていますか?
- nodeTypes/edgeTypes はレンダリングの外部で定義されていますか?
- コールバックは
useCallbackでラップされていますか? - コンテナーのサイズは適切ですか?
- スタイルはインポートされていますか?
- useReactFlow はプロバイダーの内部で使用されていますか?
- インタラクティブ要素に nodrag が指定されていますか?
- 型は全体を通して一貫して使用されていますか?