diff --git a/example/src/Layouting/index.tsx b/example/src/Layouting/index.tsx
index 8e4ce5bf..a3b0e7eb 100644
--- a/example/src/Layouting/index.tsx
+++ b/example/src/Layouting/index.tsx
@@ -8,6 +8,8 @@ import ReactFlow, {
Position,
useNodesState,
useEdgesState,
+ MarkerType,
+ EdgeMarker,
} from 'react-flow-renderer';
import dagre from 'dagre';
@@ -27,9 +29,12 @@ const LayoutFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialItems.nodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialItems.edges);
- const onConnect = useCallback((connection: Connection) => {
- setEdges((eds) => addEdge(connection, eds));
- }, []);
+ const onConnect = useCallback(
+ (connection: Connection) => {
+ setEdges((eds) => addEdge(connection, eds));
+ },
+ [setEdges]
+ );
const onLayout = (direction: string) => {
const isHorizontal = direction === 'LR';
@@ -63,6 +68,17 @@ const LayoutFlow = () => {
setNodes((nds) => nds.map((n) => ({ ...n, selected: false })));
};
+ const changeMarker = () => {
+ setEdges((eds) =>
+ eds.map((e) => ({
+ ...e,
+ markerEnd: {
+ type: (e.markerEnd as EdgeMarker)?.type === MarkerType.Arrow ? MarkerType.ArrowClosed : MarkerType.Arrow,
+ },
+ }))
+ );
+ };
+
return (
@@ -85,6 +101,7 @@ const LayoutFlow = () => {
horizontal layout
+
diff --git a/example/src/Layouting/initial-elements.ts b/example/src/Layouting/initial-elements.ts
index ce763320..539a4561 100644
--- a/example/src/Layouting/initial-elements.ts
+++ b/example/src/Layouting/initial-elements.ts
@@ -1,4 +1,4 @@
-import { Node, Edge, XYPosition } from 'react-flow-renderer';
+import { Node, Edge, XYPosition, MarkerType } from 'react-flow-renderer';
const position: XYPosition = { x: 0, y: 0 };
@@ -59,16 +59,16 @@ const nodes: Node[] = [
];
const edges: Edge[] = [
- { id: 'e12', source: '1', target: '2', type: 'smoothstep' },
- { id: 'e13', source: '1', target: '3', type: 'smoothstep' },
- { id: 'e22a', source: '2', target: '2a', type: 'smoothstep' },
- { id: 'e22b', source: '2', target: '2b', type: 'smoothstep' },
- { id: 'e22c', source: '2', target: '2c', type: 'smoothstep' },
- { id: 'e2c2d', source: '2c', target: '2d', type: 'smoothstep' },
+ { id: 'e12', source: '1', target: '2', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e13', source: '1', target: '3', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e22a', source: '2', target: '2a', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e22b', source: '2', target: '2b', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e22c', source: '2', target: '2c', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e2c2d', source: '2c', target: '2d', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
- { id: 'e45', source: '4', target: '5', type: 'smoothstep' },
- { id: 'e56', source: '5', target: '6', type: 'smoothstep' },
- { id: 'e57', source: '5', target: '7', type: 'smoothstep' },
+ { id: 'e45', source: '4', target: '5', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e56', source: '5', target: '6', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
+ { id: 'e57', source: '5', target: '7', type: 'smoothstep', markerEnd: { type: MarkerType.Arrow } },
];
const nodesAndEdges = { nodes, edges };
diff --git a/example/src/MultiFlows/index.tsx b/example/src/MultiFlows/index.tsx
index 0a247146..d3213fca 100644
--- a/example/src/MultiFlows/index.tsx
+++ b/example/src/MultiFlows/index.tsx
@@ -9,6 +9,7 @@ import ReactFlow, {
ReactFlowProvider,
useNodesState,
useEdgesState,
+ MarkerType,
} from 'react-flow-renderer';
import './multiflows.css';
@@ -21,12 +22,12 @@ const initialNodes: Node[] = [
];
const initialEdges: Edge[] = [
- { id: 'e1-2', source: '1', target: '2', animated: true },
+ { id: 'e1-2', source: '1', target: '2', animated: true, markerEnd: { type: MarkerType.Arrow } },
{ id: 'e1-3', source: '1', target: '3' },
];
-const Flow: FC = () => {
- const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
+const Flow: FC<{ id: string }> = ({ id }) => {
+ const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Edge | Connection) => setEdges((eds) => addEdge(params, eds));
@@ -39,6 +40,7 @@ const Flow: FC = () => {
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
+ id={id}
>
@@ -48,8 +50,8 @@ const Flow: FC = () => {
const MultiFlows: FC = () => (
-
-
+
+
);
diff --git a/src/components/Edges/wrapEdge.tsx b/src/components/Edges/wrapEdge.tsx
index 7f2a66b9..4bc74835 100644
--- a/src/components/Edges/wrapEdge.tsx
+++ b/src/components/Edges/wrapEdge.tsx
@@ -53,6 +53,7 @@ export default (EdgeComponent: ComponentType) => {
onEdgeUpdateEnd,
markerEnd,
markerStart,
+ rfId,
}: WrapEdgeProps): JSX.Element | null => {
const [updating, setUpdating] = useState(false);
const { addSelectedEdges, connectionMode } = useStore(selector, shallow);
@@ -120,8 +121,8 @@ export default (EdgeComponent: ComponentType) => {
const onEdgeUpdaterMouseEnter = () => setUpdating(true);
const onEdgeUpdaterMouseOut = () => setUpdating(false);
- const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart)})`, [markerStart]);
- const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd)})`, [markerEnd]);
+ const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart, rfId)})`, [markerStart, rfId]);
+ const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd, rfId)})`, [markerEnd, rfId]);
if (hidden) {
return null;
diff --git a/src/container/EdgeRenderer/MarkerDefinitions.tsx b/src/container/EdgeRenderer/MarkerDefinitions.tsx
index dcae27bf..4075ec2e 100644
--- a/src/container/EdgeRenderer/MarkerDefinitions.tsx
+++ b/src/container/EdgeRenderer/MarkerDefinitions.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo } from 'react';
+import React, { memo, useCallback } from 'react';
import { useStore } from '../../store';
import { EdgeMarker, ReactFlowState } from '../../types';
@@ -9,6 +9,7 @@ interface MarkerProps extends EdgeMarker {
}
interface MarkerDefinitionsProps {
defaultColor: string;
+ rfId?: string;
}
const Marker = ({
@@ -40,26 +41,36 @@ const Marker = ({
);
};
-const edgesSelector = (s: ReactFlowState) => s.edges;
-
-const MarkerDefinitions = ({ defaultColor }: MarkerDefinitionsProps) => {
- const edges = useStore(edgesSelector);
- const markers = useMemo(() => {
+const markerSelector =
+ ({ defaultColor, rfId }: { defaultColor: string; rfId?: string }) =>
+ (s: ReactFlowState) => {
const ids: string[] = [];
- return edges.reduce((markers, edge) => {
- [edge.markerStart, edge.markerEnd].forEach((marker) => {
- if (marker && typeof marker === 'object') {
- const markerId = getMarkerId(marker);
- if (!ids.includes(markerId)) {
- markers.push({ id: markerId, color: marker.color || defaultColor, ...marker });
- ids.push(markerId);
+ return s.edges
+ .reduce((markers, edge) => {
+ [edge.markerStart, edge.markerEnd].forEach((marker) => {
+ if (marker && typeof marker === 'object') {
+ const markerId = getMarkerId(marker, rfId);
+ if (!ids.includes(markerId)) {
+ markers.push({ id: markerId, color: marker.color || defaultColor, ...marker });
+ ids.push(markerId);
+ }
}
- }
- });
- return markers.sort((a, b) => a.id.localeCompare(b.id));
- }, []);
- }, [edges, defaultColor]);
+ });
+ return markers;
+ }, [])
+ .sort((a, b) => a.id.localeCompare(b.id));
+ };
+
+// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
+// when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
+// that we can then use for creating our unique marker ids
+const MarkerDefinitions = ({ defaultColor, rfId }: MarkerDefinitionsProps) => {
+ const markers = useStore(
+ useCallback(markerSelector({ defaultColor, rfId }), [defaultColor, rfId]),
+ // the id includes all marker options, so we just need to look at that part of the marker
+ (a, b) => !(a.length !== b.length || a.some((m, i) => m.id !== b[i].id))
+ );
return (
@@ -82,4 +93,4 @@ const MarkerDefinitions = ({ defaultColor }: MarkerDefinitionsProps) => {
MarkerDefinitions.displayName = 'MarkerDefinitions';
-export default MarkerDefinitions;
+export default memo(MarkerDefinitions);
diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx
index 4ddb9909..98f90b1d 100644
--- a/src/container/EdgeRenderer/index.tsx
+++ b/src/container/EdgeRenderer/index.tsx
@@ -40,6 +40,7 @@ interface EdgeRendererProps {
edgeUpdaterRadius?: number;
noPanClassName?: string;
elevateEdgesOnSelect: boolean;
+ rfId?: string;
}
const selector = (s: ReactFlowState) => ({
@@ -93,7 +94,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
height={height}
className="react-flow__edges react-flow__container"
>
- {isMaxLevel && }
+ {isMaxLevel && }
{edges.map((edge: Edge) => {
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(nodeInternals, edge.source);
@@ -184,6 +185,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
onEdgeDoubleClick={props.onEdgeDoubleClick}
onEdgeUpdateStart={props.onEdgeUpdateStart}
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
+ rfId={props.rfId}
/>
);
})}
diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx
index e5db5604..3ef21baf 100644
--- a/src/container/GraphView/index.tsx
+++ b/src/container/GraphView/index.tsx
@@ -84,6 +84,7 @@ const GraphView = ({
noWheelClassName,
noPanClassName,
elevateEdgesOnSelect,
+ id,
}: GraphViewProps) => {
useOnInitHandler(onInit);
@@ -139,6 +140,7 @@ const GraphView = ({
defaultMarkerColor={defaultMarkerColor}
noPanClassName={noPanClassName}
elevateEdgesOnSelect={!!elevateEdgesOnSelect}
+ rfId={id}
/>
(
noWheelClassName={noWheelClassName}
noPanClassName={noPanClassName}
elevateEdgesOnSelect={elevateEdgesOnSelect}
+ id={rest?.id}
/>
{
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void;
markerStart?: EdgeMarkerType;
markerEnd?: EdgeMarkerType;
+ rfId?: string;
}
export interface EdgeSmoothStepProps extends EdgeProps {
diff --git a/src/utils/graph.ts b/src/utils/graph.ts
index a426baed..fce21b3c 100644
--- a/src/utils/graph.ts
+++ b/src/utils/graph.ts
@@ -30,7 +30,7 @@ export const getIncomers = (node: Node, nodes: Node
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): string =>
`reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
-export const getMarkerId = (marker: EdgeMarkerType | undefined): string => {
+export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string): string => {
if (typeof marker === 'undefined') {
return '';
}
@@ -39,10 +39,12 @@ export const getMarkerId = (marker: EdgeMarkerType | undefined): string => {
return marker;
}
- return Object.keys(marker)
+ const idPrefix = rfId ? `${rfId}__` : '';
+
+ return `${idPrefix}${Object.keys(marker)
.sort()
.map((key: string) => `${key}=${(marker as any)[key]}`)
- .join('&');
+ .join('&')}`;
};
const connectionExists = (edge: Edge, edges: Edge[]) => {