はじめに
ReactFlowのインストールや基礎はこちらをご覧ください。
双方向ハンドルとは
ハンドルとは
ReactFlowのノードとノードを線でつなぐ時、そのつなぐポイントの事を言います。
通常、ReactFlowのカスタムノードという機能を使ってノードをカスタマイズする時、ハンドルの位置を決めることが出来ます。
その時、同時にハンドルが送信側(source)なのか受信側(target)なのかを決める必要があります。
送信側同士で線をつなぐことも受信側から線をつなぐことも出来ません。
しかし、場合によっては一つのハンドルから送信側も受信側も両方行いたい時ってありますよね。
そんな時に用いるのが双方向ハンドルです
実装
app/page.tsxは基本的なカスタムノードの使い方です。
CustomNode.tsxはやってることはとてもシンプルで送信ハンドルと受診ハンドルを重ねているだけです。
こんな単純な事で解決できるんですね
// app/page.tsx
'use client';
import { useCallback, useState, type ChangeEventHandler } from 'react';
import {
ReactFlow,
addEdge,
useNodesState,
useEdgesState,
MiniMap,
Background,
Controls,
Panel,
type Node,
type Edge,
type ColorMode,
type OnConnect,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import './page.css';
import CustomNode from './components/CustomNode';
const nodeTypes = {
customNode: CustomNode,
};
const initialNodes: Node[] = [
{
id: 'A',
type: 'customNode',
position: { x: 0, y: 150 },
data: { label: 'A' },
},
{
id: 'B',
type: 'customNode',
position: { x: 250, y: 0 },
data: { label: 'B' },
},
{
id: 'C',
type: 'customNode',
position: { x: 250, y: 150 },
data: { label: 'C' },
},
{
id: 'D',
type: 'customNode',
position: { x: 250, y: 300 },
data: { label: 'D' },
},
];
const initialEdges: Edge[] = [
];
export default function FlowChart() {
const [colorMode, setColorMode] = useState<ColorMode>('dark');
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect: OnConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges],
);
const onChange: ChangeEventHandler<HTMLSelectElement> = (evt) => {
setColorMode(evt.target.value as ColorMode);
};
return (
<div style={{ height: '500px', width: '100%' }} >
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
colorMode={colorMode}
nodeTypes={nodeTypes}
fitView
>
<MiniMap />
<Background />
<Controls />
<Panel position="top-right">
<select
className="xy-theme__select"
onChange={onChange}
data-testid="colormode-select"
>
<option value="dark">dark</option>
<option value="light">light</option>
<option value="system">system</option>
</select>
</Panel>
</ReactFlow>
</div>
);
};
// app\components\CustomNode.tsx
import React from 'react';
import { Handle, Position } from '@xyflow/react';
interface CustomNodeProps {
data: {
label: string;
};
}
export default function CustomNode({ data }: CustomNodeProps) {
return (
<div style={{
padding: '10px',
border: '2px solid #ccc',
borderRadius: '8px',
background: '#111',
minWidth: '100px',
textAlign: 'center',
position: 'relative'
}}>
{/* 上のハンドル */}
<Handle
type="source"
position={Position.Top}
id="top"
style={{ background: '#555' }}
/>
<Handle
type="target"
position={Position.Top}
id="top"
style={{ background: '#555' }}
/>
{/* 右のハンドル */}
<Handle
type="source"
position={Position.Right}
id="right"
style={{ background: '#555' }}
/>
<Handle
type="target"
position={Position.Right}
id="right"
style={{ background: '#555' }}
/>
{/* 下のハンドル */}
<Handle
type="source"
position={Position.Bottom}
id="bottom"
style={{ background: '#555' }}
/>
<Handle
type="target"
position={Position.Bottom}
id="bottom"
style={{ background: '#555' }}
/>
{/* 左のハンドル */}
<Handle
type="source"
position={Position.Left}
id="left"
style={{ background: '#555' }}
/>
<Handle
type="target"
position={Position.Left}
id="left"
style={{ background: '#555' }}
/>
<div>{data.label}</div>
</div>
);
}
終わりに
とても単純ですが、双方向ハンドルの実装が出来ました。