v10への移行
React Flow v10へようこそ!メジャーバージョンアップデートに伴い、多くの新機能が追加されましたが、いくつかの破壊的変更もあります。
新機能
- サブフロー:親ノードにノードを追加し、グループやネストされたフローを作成できるようになりました。
- ノードタイプ「group」:ハンドルを持たない新しいノードタイプで、グループノードとして使用できます。
- タッチデバイスサポート:タッチデバイスでノードを接続できるようになりました。
- 初期化時のビューのフィット:新しい
fitView
プロップを使用して、初期ビューにフィットさせることができます。 - キー操作:単一キーだけでなく、複数のキーやキーの組み合わせも可能になりました。
- useKeyPressフック:キーボードイベントを処理するためのユーティリティフックです。
- useReactFlowフック:フローを操作する関数を公開するReact Flowインスタンスを返します。
- useNodes、useEdges、useViewportフック:ノード、エッジ、ビューポートを受け取るためのフックです。
- エッジマーカー:エッジの開始および終了マーカーを構成するためのより多くのオプション。
破壊的変更
要約
elements
配列をnodes
配列とedges
配列に分割し、onNodesChange
とonEdgesChange
ハンドラーを実装します(詳細はコアコンセプトセクションを参照)。- カスタム
nodeTypes
とedgeTypes
をメモ化してください。 onLoad
をonInit
に名前変更paneMoveable
をpanOnDrag
に名前変更useZoomPanHelper
をuseReactFlow
に名前変更(およびsetTransform
をsetViewport
に)- ノードとエッジのオプション
isHidden
をhidden
に名前変更
破壊的変更の詳細な説明
1. 要素 - ノードとエッジ
多くの人が半制御された elements
プロップで苦労しているのを見てきました。ローカルのユーザー状態とReact Flowの内部状態を同期させるのは常に少し面倒でした。ドキュメント化されていなかった内部ストアを使用していた人もおり、常にハッキーな解決策でした。新バージョンでは、React Flowを使用する2つの方法を提供します - 非制御と制御です。
1.1. 制御された nodes
と edges
完全な制御を行い、ローカルの状態またはストアからノードとエッジを使用する場合は、onNodesChange
とonEdgesChange
ハンドラーと組み合わせてnodes
、edges
プロップを使用できます。インタラクティブなフローにはこれらのハンドラーを実装する必要があります(パンとズームだけで良い場合は必要ありません)。ノードが初期化、ドラッグ、選択、または削除されるときに変更を受け取ります。つまり、ノードの正確な位置と寸法、または選択されているかどうかを常に知ることができます。変更を適用するために使用するヘルパー関数 applyNodeChanges
と applyEdgeChanges
をエクスポートします。
旧API
import { useState, useCallback } from 'react';
import { ReactFlow, removeElements, addEdge } from 'react-flow-renderer';
const initialElements = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
{ id: 'e1-2', source: '1', target: '2' },
];
const BasicFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = useCallback(
(elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els)),
[],
);
const onConnect = useCallback((connection) =>
setElements((es) => addEdge(connection, es)),
);
return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
/>
);
};
export default BasicFlow;
新API
import { useState, useCallback } from 'react';
import {
ReactFlow,
applyNodeChanges,
applyEdgeChanges,
addEdge,
} from 'react-flow-renderer';
const initialNodes = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
const BasicFlow = () => {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((ns) => applyNodeChanges(changes, ns)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((es) => applyEdgeChanges(changes, es)),
[],
);
const onConnect = useCallback((connection) =>
setEdges((eds) => addEdge(connection, eds)),
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
/>
);
};
export default BasicFlow;
クイックスタートには、新しいフック useNodesState
と useEdgesState
も使用できます。
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
関連する変更
onElementsClick
->onNodeClick
とonEdgeClick
onElementsRemove
->onNodesChange
とonEdgesChange
ハンドラーで置き換えられました。
1.2 非制御された defaultNodes
と defaultEdges
最も簡単に始める方法は、defaultNodes
プロパティと defaultEdges
プロパティを使用することです。これらのプロパティを設定すると、すべてのアクションが内部的に処理されます。ノードのドラッグ、ノードの接続、ノードとエッジの削除を可能にする、完全にインタラクティブなフローを得るために、他のハンドラーを追加する必要はありません。
新しいAPI
import ReactFlow from 'react-flow-renderer';
const defaultNodes = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
];
const defaultEdges = [{ id: 'e1-2', source: '1', target: '2' }];
const BasicFlow = () => {
return <ReactFlow defaultNodes={defaultNodes} defaultEdges={defaultEdges} />;
};
export default BasicFlow;
ノードまたはエッジを追加、削除、または更新する場合は、新しいuseReactFlow
フックを使用するか、インスタンスを関数パラメーターとして取得するonInit
ハンドラーを使用することで取得できるReactFlowインスタンス を使用してのみ実行できます。
2. カスタムnodeTypes
とedgeTypes
のメモ化
新しいノードタイプまたはエッジタイプを渡すたびに、バックグラウンドでラップされたノードまたはエッジコンポーネントタイプが作成されます。つまり、毎回レンダリングするたびに新しいnodeType
またはedgeType
オブジェクトを作成しないでください。**nodeTypesとedgeTypesをメモ化するか、変更されない場合はコンポーネントの外で定義してください。**
このようにしないでください
これにより、毎回のレンダリングで新しいオブジェクトが作成され、バグとパフォーマンスの問題につながります。
// this is bad! Don't do it.
<ReactFlow
nodes={[]}
nodeTypes={{
specialType: SpecialNode, // bad!
}}
/>
このようにしてください
function Flow() {
const nodeTypes = useMemo(() => ({ specialType: SpecialNode }), []);
return <ReactFlow nodes={[]} nodeTypes={nodeTypes} />;
}
または、変更されない場合はコンポーネントの外でタイプを作成します。
const nodeTypes = { specialType: SpecialNode };
function Flow() {
return <ReactFlow nodes={[]} nodeTypes={nodeTypes} />;
}
3. Redux - Zustand
状態管理ライブラリをReduxからZustandに変更しました。この変更により、状態関連のコードから約300LOCを削除することができました。内部ストアにアクセスする必要がある場合は、useStore
フックを使用できます。
古いAPI
import { useStoreState, useStoreActions } from 'react-flow-renderer';
...
const transform = useStoreState((store) => store.transform);
新しいAPI
import { useStore } from 'react-flow-renderer';
...
const transform = useStore((store) => store.transform);
内部ストアにアクセスする場合は、コンポーネントを<ReactFlowProvider />
でラップする必要があります。
再レンダリングをトリガーせずに、たとえばイベントハンドラーでストアを取得する必要がある場合に備えて、useStoreApi
もエクスポートしています。
import { useStoreApi } from 'react-flow-renderer';
...
const store = useStoreApi();
...
// in an event handler
const [x, y, zoom] = store.getState().transform;
4. onLoad - onInit
onLoad
コールバックはonInit
に名前が変更され、ノードが初期化されたときに実行されるようになりました。
古いAPI
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.zoomTo(2);
...
<ReactFlow
...
onLoad={onLoad}
/>
新しいAPI
const onInit = (reactFlowInstance: ReactFlowInstance) => reactFlowInstance.zoomTo(2);
...
<ReactFlow
...
onInit={onInit}
/>
5. paneMoveable - panOnDrag
これはAPIの他の部分(panOnScroll
、zoomOnScroll
など)とより整合性があります。
古いAPI
<ReactFlow
...
paneMoveable={false}
/>
新しいAPI
<ReactFlow
...
panOnDrag={false}
/>
6. useZoomPanHelper transform - useReactFlow
に統合
「transform」はストア内の変換の変数名でもあるため、transform
がセッターであることが明確ではないため、setViewport
に名前を変更しました。これは他の関数ともより整合性があります。また、すべてのuseZoomPanHelper
関数は、useReactFlow
フックまたはonInit
ハンドラーから取得するReact Flowインスタンスに移動されました。
古いAPI
const { transform, setCenter, setZoom } = useZoomPanHelper();
...
transform({ x: 100, y: 100, zoom: 2 });
新しいAPI
const { setViewport, setCenter, setZoom } = useReactFlow();
...
setViewport({ x: 100, y: 100, zoom: 2 });
新しいビューポート関数
getZoom
getViewport
7. isHidden - hidden
プレフィックス付き(is...
)とプレフィックスなしのブール値オプション名を混在させていました。すべてのノードとエッジのオプションには、プレフィックスが付けられなくなりました。そのため、hidden
、animated
、selected
、draggable
、selectable
、connectable
となります。
古いAPI
const hiddenNode = { id: '1', isHidden: true, position: { x: 50, y: 50 } };
新しいAPI
const hiddenNode = { id: '1', hidden: true, position: { x: 50, y: 50 } };
8. arrowHeadType markerEndId - markerStart / markerEnd
エッジのマーカーをカスタマイズするためのAPIを改善しました。新しいAPIを使用すると、エッジの先頭と末尾に個々のマーカーを設定し、色、strokeWidthなどでカスタマイズできます。markerEndIdを設定することもできますが、異なるプロパティを使用する代わりに、markerStart
とmarkerEnd
プロパティは、文字列(自分で定義する必要があるsvgマーカーのID)または組み込みのarrowclosedまたはarrowマーカーを使用するための構成オブジェクトのいずれかを受け入れます。
古いAPI
const markerEdge = { source: '1', target: '2', arrowHeadType: 'arrow' };
新しいAPI
const markerEdge = {
source: '1',
target: '2',
markerStart: 'myCustomSvgMarker',
markerEnd: { type: 'arrow', color: '#f00' },
};
9. ArrowHeadType - MarkerType
これは、マーカーAPIをより一貫性のあるものにするための言葉の変更にすぎません。今ではエッジの先頭にマーカーを設定できるようになったため、ArrowHeadTypeという名前のタイプはMarkerTypeに名前が変更されました。将来的には、矢印の形状だけでなく、円、菱形なども含めることができます。
10. 属性表示
これはAPIに対する破壊的な変更ではありませんが、React Flowの一般的な外観に小さな変更があります。「React Flow」という小さな属性表示が右下に追加されました(位置はattributionPosition
プロパティで設定できます)。この変更は、新しい「React Flow Pro」サブスクリプションモデルに伴うものです。商用アプリケーションで属性表示を削除する場合は、「React Flow Pro」を購読してください。