import React, { memo, useMemo, ComponentType, useEffect, useRef } from 'react'; import shallow from 'zustand/shallow'; import useVisibleNodes from '../../hooks/useVisibleNodes'; import { useStore } from '../../store'; import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types'; import { internalsSymbol } from '../../utils'; interface NodeRendererProps { nodeTypes: NodeTypesWrapped; selectNodesOnDrag: boolean; onNodeClick?: NodeMouseHandler; onNodeDoubleClick?: NodeMouseHandler; onNodeMouseEnter?: NodeMouseHandler; onNodeMouseMove?: NodeMouseHandler; onNodeMouseLeave?: NodeMouseHandler; onNodeContextMenu?: NodeMouseHandler; onlyRenderVisibleElements: boolean; noPanClassName: string; noDragClassName: string; } const selector = (s: ReactFlowState) => ({ nodesDraggable: s.nodesDraggable, nodesConnectable: s.nodesConnectable, elementsSelectable: s.elementsSelectable, updateNodeDimensions: s.updateNodeDimensions, }); const NodeRenderer = (props: NodeRendererProps) => { const { nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions } = useStore(selector, shallow); const nodes = useVisibleNodes(props.onlyRenderVisibleElements); const resizeObserverRef = useRef(); const resizeObserver = useMemo(() => { if (typeof ResizeObserver === 'undefined') { return null; } const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => { const updates = entries.map((entry: ResizeObserverEntry) => ({ id: entry.target.getAttribute('data-id') as string, nodeElement: entry.target as HTMLDivElement, forceUpdate: true, })); updateNodeDimensions(updates); }); resizeObserverRef.current = observer; return observer; }, []); useEffect(() => { return () => { resizeObserverRef?.current?.disconnect(); }; }, []); return (
{nodes.map((node) => { let nodeType = node.type || 'default'; if (!props.nodeTypes[nodeType]) { // @ts-ignore if (process.env.NODE_ENV === 'development') { console.warn( `[React Flow]: Node type "${nodeType}" not found. Using fallback type "default". Help: https://reactflow.dev/error#300` ); } nodeType = 'default'; } const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType; const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined')); const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined')); return (
); }; NodeRenderer.displayName = 'NodeRenderer'; export default memo(NodeRenderer);