diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index 28323a19..5973751d 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -101,12 +101,12 @@ export function EdgeWrapper({ ); const markerStartUrl = useMemo( - () => (edge.markerStart ? `url(#${getMarkerId(edge.markerStart, rfId)})` : undefined), + () => (edge.markerStart ? `url('#${getMarkerId(edge.markerStart, rfId)}')` : undefined), [edge.markerStart, rfId] ); const markerEndUrl = useMemo( - () => (edge.markerEnd ? `url(#${getMarkerId(edge.markerEnd, rfId)})` : undefined), + () => (edge.markerEnd ? `url('#${getMarkerId(edge.markerEnd, rfId)}')` : undefined), [edge.markerEnd, rfId] ); diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index fa3ec791..548a4aa4 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -84,22 +84,28 @@ export function NodeWrapper({ const computedWidth = node.computed?.width; const computedHeight = node.computed?.height; const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height); + const hasHandleBounds = !!node[internalsSymbol]?.handleBounds; const moveSelectedNodes = useMoveSelectedNodes(); + useEffect(() => { + return () => { + if (nodeRef.current) { + resizeObserver?.unobserve(nodeRef.current); + } + }; + }, []); + useEffect(() => { if (nodeRef.current && !node.hidden) { const currNode = nodeRef.current; - if (!initialized) { - resizeObserver?.observe(currNode); - } else { + if (!initialized || !hasHandleBounds) { resizeObserver?.unobserve(currNode); + resizeObserver?.observe(currNode); } - - return () => resizeObserver?.unobserve(currNode); } - }, [node.hidden, initialized]); + }, [node.hidden, initialized, hasHandleBounds]); useEffect(() => { // when the user programmatically changes the source or handle position, we re-initialize the node diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 96080368..f1d68e6c 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -9,7 +9,7 @@ import { infiniteExtent, type CoordinateExtent } from '@xyflow/system'; import { useStore, useStoreApi } from '../../hooks/useStore'; import type { Node, Edge, ReactFlowState, ReactFlowProps, FitViewOptions } from '../../types'; -import { initNodeOrigin } from '../../container/ReactFlow'; +import { defaultNodeOrigin } from '../../container/ReactFlow/init-values'; // these fields exist in the global store and we need to keep them up to date const reactFlowFieldsToTrack = [ @@ -84,6 +84,19 @@ const selector = (s: ReactFlowState) => ({ setDefaultNodesAndEdges: s.setDefaultNodesAndEdges, }); +const initPrevValues = { + // these are values that are also passed directly to other components + // than the StoreUpdater. We can reduce the number of setStore calls + // by setting the same values here as prev fields. + translateExtent: infiniteExtent, + nodeOrigin: defaultNodeOrigin, + minZoom: 0.5, + maxZoom: 2, + elementsSelectable: true, + noPanClassName: 'nopan', + rfId: '1', +}; + export function StoreUpdater(props: StoreUpdaterProps) { const { setNodes, @@ -99,23 +112,15 @@ export function StoreUpdater(props: StoreUpdaterProps) { useEffect(() => { setDefaultNodesAndEdges(props.defaultNodes, props.defaultEdges); + return () => { + // when we reset the store we also need to reset the previous fields + previousFields.current = initPrevValues; reset(); }; }, []); - const previousFields = useRef>({ - // these are values that are also passed directly to other components - // than the StoreUpdater. We can reduce the number of setStore calls - // by setting the same values here as prev fields. - translateExtent: infiniteExtent, - nodeOrigin: initNodeOrigin, - minZoom: 0.5, - maxZoom: 2, - elementsSelectable: true, - noPanClassName: 'nopan', - rfId: '1', - }); + const previousFields = useRef>(initPrevValues); useEffect( () => { diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index df9936a1..6448abe4 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -1,14 +1,6 @@ import { forwardRef, type CSSProperties } from 'react'; import cc from 'classcat'; -import { - ConnectionLineType, - PanOnScrollMode, - SelectionMode, - infiniteExtent, - isMacOs, - type NodeOrigin, - type Viewport, -} from '@xyflow/system'; +import { ConnectionLineType, PanOnScrollMode, SelectionMode, infiniteExtent, isMacOs } from '@xyflow/system'; import { A11yDescriptions } from '../../components/A11yDescriptions'; import { Attribution } from '../../components/Attribution'; @@ -18,9 +10,7 @@ import { useColorModeClass } from '../../hooks/useColorModeClass'; import { GraphView } from '../GraphView'; import { Wrapper } from './Wrapper'; import type { ReactFlowProps, ReactFlowRefType } from '../../types'; - -export const initNodeOrigin: NodeOrigin = [0, 0]; -const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; +import { defaultViewport as initViewport, defaultNodeOrigin } from './init-values'; const wrapperStyle: CSSProperties = { width: '100%', @@ -89,11 +79,11 @@ const ReactFlow = forwardRef( nodesDraggable, nodesConnectable, nodesFocusable, - nodeOrigin = initNodeOrigin, + nodeOrigin = defaultNodeOrigin, edgesFocusable, edgesUpdatable, elementsSelectable = true, - defaultViewport = initDefaultViewport, + defaultViewport = initViewport, minZoom = 0.5, maxZoom = 2, translateExtent = infiniteExtent, diff --git a/packages/react/src/container/ReactFlow/init-values.ts b/packages/react/src/container/ReactFlow/init-values.ts new file mode 100644 index 00000000..75e7fcaa --- /dev/null +++ b/packages/react/src/container/ReactFlow/init-values.ts @@ -0,0 +1,4 @@ +import { type NodeOrigin, Viewport } from '@xyflow/system'; + +export const defaultNodeOrigin: NodeOrigin = [0, 0]; +export const defaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 46447e00..71a51a1e 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -303,13 +303,7 @@ const createRFStore = ({ set(currentConnection); }, - reset: () => { - // @todo: what should we do about this? Do we still need it? - // if you are on a SPA with multiple flows, we want to make sure that the store gets resetted - // when you switch pages. Does this reset solves this? Currently it always gets called. This - // leads to an emtpy nodes array at the beginning. - // set({ ...getInitialState() }); - }, + reset: () => set({ ...getInitialState() }), }), Object.is );