React hooks 项目中使用React Flow实现拖拽流程图
- 最近项目需求需要实现一个自定义流程图
- 查了下资料 加上大神推荐 最终选用React Flow
- 最开始写demo的时候是9.5的版本 后来官方api升级到10.0 用法改变了很多 参考着api修改 升级后性能提升很多
安装
npm install react-flow-renderer
使用
按照官方demo 实现拖拽功能
index.tsx
const ReactFlowDemo: React.FC<{}> = () => {
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const onConnect = useCallback((params) => setEdges((eds) => addEdge({ ...params, markerEnd: { type: MarkerType.Arrow } }, eds)), []);
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = (event: React.DragEvent) => {
event.preventDefault();
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const type = event?.dataTransfer.getData('application/reactflow');
if (typeof type === 'undefined' || !type) {
return;
}
const position = reactFlowInstance.project({
x: event.clientX - reactFlowBounds.left,
y: event.clientY - reactFlowBounds.top,
});
let newNode = {
id: getId(),
type: type,
position,
data: { label: `${type} node` },
};
setNodes((es) => es.concat(newNode));
}
const onNodeClick = (event: React.MouseEvent, node: Node) => {
let { data, id } = node
console.log("element>>", node)
};
useEffect(() => {
}, [])
return (
<PageHeaderWrapper >
<div className={styles.main}>
<div className={styles.dndflow}>
<ReactFlowProvider >
<Sidebar />
<div className={styles.reactflowWrapper} ref={reactFlowWrapper}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick}
onConnect={onConnect}
onInit={setReactFlowInstance}
onDrop={onDrop}
onDragOver={onDragOver}
defaultZoom={1.0} minZoom={1} maxZoom={1.5}
>
<Controls />
</ReactFlow>
</div>
</ReactFlowProvider>
</div>
</div>
</PageHeaderWrapper>
)
}
Sidebar.tsx
const Sidebar: React.FC<flowSiderProps> = (props) => {
// 获取画布上的节点
const onDragStart = (evt: React.DragEvent, nodeType: string) => {
// 记录被拖拽的节点类型
evt.dataTransfer.setData('application/reactflow', nodeType);
evt.dataTransfer.effectAllowed = 'move';
};
return (
<aside>
<div className="dndnode input" onDragStart={(event) => onDragStart(event, 'input')} draggable>
Input Node
</div>
<div className="dndnode" onDragStart={(event) => onDragStart(event, 'default')} draggable>
Default Node
</div>
<div className="dndnode output" onDragStart={(event) => onDragStart(event, 'output')} draggable>
Output Node
</div>
</aside>
);
}
nodes和edges是节点和线的对象数组
node:
- id: string 唯一标识,用于连线
- position: { x: number, y: number } 定位信息
- type: string 定义节点的类型
- data: {} 传入节点内的数据
自定义节点的话 修改type就可以
edge:
- id: string 唯一标识,必填
- source: string 连线的起始节点的 id
- target: string 连线的结束节点的 id
- type: string 线的类型
线的类型有曲线、直线、折线等
拓展
使用中节点类型可能不满足目前要求 可以自定义类型 节点的形状、颜色、选中状态都可以自己调整
index.tsx
const nodeTypes = {
floating: FloatingNode,
};
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick}
onConnect={onConnect}
onInit={setReactFlowInstance}
onDrop={onDrop}
onDragOver={onDragOver}
nodeTypes={nodeTypes}
defaultZoom={1.0} minZoom={1} maxZoom={1.5}
>
<Controls />
</ReactFlow>
Sidebar.tsx
<div className="dndnode input" onDragStart={(event) => onDragStart(event, 'input')} draggable>
Input Node
</div>
<div className="dndnode" onDragStart={(event) => onDragStart(event, 'default')} draggable>
Default Node
</div>
<div className="dndnode output" onDragStart={(event) => onDragStart(event, 'output')} draggable>
Output Node
</div>
<div className="floating output" onDragStart={(event) => onDragStart(event, 'floating')} draggable>
Floating Node
</div>
FloatingNode.tsx
export default memo((node: Node) => {
const { data: { label } } = node
return (
<>
<div>
{label}
</div>
<Handle type="target" position={Position.Top} id="a" />
<Handle type="source" position={Position.Bottom} id="b" />
<Handle type="source" position={Position.Right} id="c" />
<Handle type="target" position={Position.Left} id="d" />
</>
);
})