react-flow備忘録
まず・・
このページにあるこのコマンドが一番手っ取り早い
npx degit xyflow/vite-react-flow-template your-app-name
こんな感じでお手本コードが手に入る
概念整理とかなんとか
重要コンポーネント
ReactFlow
簡単な例
- nodesには複数のnodeを配列の中にオブジェクトを配置して渡す
nodes
dataは'label'で表示する文字列を指定できる
positionで画面のどの位置に設定するか指定できる
typeは下記の通り。
- "default"
- "input"
- "output"
- "group"
が、オーバーライド可能。
edges
const edges = [{ id: '1-2', source: '1', target: '2' }];
デフォルトだと下記のようになる。
const edges = [
{ id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
];
typeを変更できる。
import { ReactFlow, Controls, Background } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const nodes = [
{
id: '1',
data: { label: 'Hello' },
position: { x: 0, y: 0 },
type: 'input',
},
{
id: '2',
data: { label: 'World' },
position: { x: 100, y: 100 },
},
];
function Flow() {
return (
<div style={{ height: '100%' }}>
<ReactFlow nodes={nodes}>
<Background />
<Controls />
</ReactFlow>
</div>
);
}
export default Flow;
TODO:nodeタイプのオーバーライド方法
ReactFlowコンポーネントの重要な引数
- onNodesChange
- onEdgesChange
使い方
import { useState, useCallback } from 'react';
import { ReactFlow, applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
いつもの初期化(initialNodes,initialEdgesは任意で配列・オブジェクトの構造で渡す)
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[],
);
- setNodes, setEdges関数をimportする
- useCallbackでsetNodes,setEdgesの引数にapply〇〇Changesを渡す
- 関数内で定義することが前提
- ReactFlowコンポーネントに渡す
- 多分おすすめはまんまonNodesChange, onEdgesChangeで定義しちゃうことかも。
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
onEdgesChange={onEdgesChange}
fitView
>
<Background />
<Controls />
</ReactFlow>
公式の説明を引用すると
you’ll be able to click and drag the components, and the UI will update based on those movements.
つまり、onNodesChange, onEdgesChangeの上記書き方をすることでドラッグ可能となる。
ReactFlowコンポーネントの重要な引数 その2
onConnect
使い方としてはこう。
つまり、useCallback内部でsetEdgesの実行時にaddEdgeを呼び出す。
多分わかりやすいのはonConnectという関数で作ってそのままReactFlowコンポーネント引数に渡すこと。
import { useState, useCallback } from 'react';
import {
ReactFlow,
Controls,
Background,
applyNodeChanges,
applyEdgeChanges,
addEdge,
} from '@xyflow/react';
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[],
);
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
>
<Background />
<Controls />
</ReactFlow>
</div>
Custom Nodesについて
custom nodeを定義する際、Handleコンポーネントを利用する。
The <Handle /> component is used in your custom nodes to define connection points.
接合点とでも訳そうか。
ノードから生える点を作れる。
import { Handle, Position } from '@xyflow/react';
export CustomNode = ({ data }) => {
return (
<>
<div style={{ padding: '10px 20px' }}>
{data.label}
</div>
<Handle type="target" position={Position.Left} />
<Handle type="source" position={Position.Right} />
</>
);
};
使い方を見てみる。
import { useCallback, useState } from 'react';
import {
ReactFlow,
addEdge,
applyEdgeChanges,
applyNodeChanges,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import TextUpdaterNode from './TextUpdaterNode';
const rfStyle = {
backgroundColor: '#B8CEFF',
};
const initialNodes = [
{
id: 'node-1',
type: 'textUpdater',
position: { x: 0, y: 0 },
data: { value: 123 },
},
];
// we define the nodeTypes outside of the component to prevent re-renderings
// you could also use useMemo inside the component
const nodeTypes = { textUpdater: TextUpdaterNode };
function Flow() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState([]);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes],
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges],
);
const onConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
style={rfStyle}
/>
);
}
export default Flow;
// we define the nodeTypes outside of the component to prevent re-renderings
// you could also use useMemo inside the component
とある。つまり関数の外でnodeTypesを定義する必要がある。
関数内でやる場合はuseMemoで書きましょうという話になる。
書き方はこう。
const nodeTypes = useMemo(() => ({ textUpdater: TextUpdaterNode }), []);
意味的に小さくてフラグメントなメモ
テーマはここからコントロールできるらしい。
ノードの中にノードを詰めるならこれが良さげ。
export const initialNodes = [
{
id: 'A',
type: 'group',
data: { label: null },
position: { x: 0, y: 0 },
style: {
width: 170,
height: 140,
},
},
{
id: 'B',
type: 'input',
data: { label: 'child node 1' },
position: { x: 10, y: 10 },
parentId: 'A',
extent: 'parent',
},
{
id: 'C',
data: { label: 'child node 2' },
position: { x: 10, y: 90 },
parentId: 'A',
extent: 'parent',
},
];
つまり、extent: 'parent' と書いた上で parentId を指定すれば、ノード同士で親子関係が作成できる。
他いい感じにやる方法はこちら。