上一篇 《React Flow使用指南(一)》介绍了自定义节点等基本操作,接下来就该实现一个真正的流程图了。
父组件的设计
如何实现外部访问内部数据?
<ReactFlowProvider />组件是一个 上下文提供程序 ,它使得在组件外部访问内部状态成为可能,并且reactflow提供的许多钩子都依赖于此组件才能工作。更多hook
function Flow() {
return (
<ReactFlowProvider>
<ComponentsSideBar />
<FlowContent />
</ReactFlowProvider>
);
}
export function ComponentsSideBar() {
...
// 该hook只有在使用的组件是<ReactFlowProvider /> 的子组件时才会起作用
const nodes = useNodes() // 获取全部node节点
const edges = useEdges(); // 获取全部edge边
const nodes = useReactFlow();
nodes.setNodes((prev) => prev.map((node) => ({ ...node, position: { x: node.position.x + 10, y: node.position.y } })))
// 该hook返回一个ReactFlowInstance可用于更新节点和边、操作视口或查询流的当前状态的钩子。
...
}
拖拽添加节点
简单的拖拽添加节点,可以通过原生 API draggable 实现。 在 SideBar 组件中触发节点的 onDragStart 事件,然后在 Flow组件中通过 ReactFlow onDrop 来接收。
// components-side-bar.tsx
export function ComponentsSideBar() {
const onDragStart = (
event: React.DragEvent<HTMLDivElement>,
nodeType: string,
data?: any,
) => {
// 拖拽的节点类型以及节点默认数据,'application/reactflow'确保拖拽时设置的数据类型与读取时一致
event.dataTransfer.setData('application/reactflow',JSON.stringify({ nodeType, data }));
event.dataTransfer.effectAllowed = 'move';
};
const style = {
width: 100,
border: '1px solid #d6d4d4',
borderRadius: 4,
padding: '4px',
marginBottom: '10px',
};
return (
<div style={{ width: 120, border: '1px solid #d6d4d4', padding: '10px' }}>
<div
style={style}
onDragStart={event => onDragStart(event, 'rounded', { num: 1 })}
draggable
>
Rounded Node
</div>
<div
style={style}
onDragStart={event => onDragStart(event, 'input')}
draggable
>
Input Node
</div>
<div
style={style}
onDragStart={event => onDragStart(event, 'output')}
draggable
>
Output Node
</div>
</aside>
);
}
// flow.tsx
...
export function FlowContent(props: ReactFlowProps) {
...
const { screenToFlowPosition } = useReactFlow();
// `onDrop` 和 `onDragOver` 是用于处理外部元素拖放到画布的关键事件。
// onDragOver当拖动的元素悬停在画布上时触发,必须调用event.preventDefault()以允许放置。
const onDragOver = (event: React.DragEvent) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
};
// 当元素被释放到画布时触发,用于创建新节点。
const onDrop = (event: React.DragEvent) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
if (!type) {
return;
}
try {
// 解析json串
const { data, nodeType } = JSON.parse(type);
// 将屏幕坐标转换为画布坐标
const position = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode = {
id: getId(),
position,
type: nodeType,
data,
};
setNodes(nds => nds.concat(newNode));
} catch (e) {
console.log(e);
}
};
return (
<div className={cx('react-flow')}>
<ReactFlow
...
onDrop={onDrop}
onDragOver={onDragOver}
{...props}
/>
</div>
);
}
连线
在画布上连线的时候,会触发 ReactFlow onConnect 事件,并提供连线信息。
export function FlowContent(props: ReactFlowCustomizeProps) {
...
const onConnect = useCallback(
(params: Connection) => setEdges(eds => addEdge(params, eds)),
[],
);
return (
<div className={cx('react-flow')}>
<ReactFlow onConnect={onConnect} .../>
</div>
);
}
如果需要设置连线类型,或者设置其他连线的信息,都可以通过 addEdge 的第一个参数来设置。 如果需要自定义连接中的线的样式,可以使用 connectionLineComponent,具体可以参考官方示例