From 6630935d9ee8060464b197ad13f85c30902b7bc5 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 22 Feb 2022 18:33:59 +0100 Subject: [PATCH] refactor(nodeTypes/edgeTypes): show warning when not memoized closes #1620 --- example/src/CustomNode/index.tsx | 1 + example/src/Edges/index.tsx | 4 +-- example/src/FloatingEdges/index.tsx | 4 +-- example/src/NodeTypesObjectChange/index.tsx | 4 +-- example/src/Undirectional/index.tsx | 4 +-- example/src/UseUpdateNodeInternals/index.tsx | 4 +-- example/src/Validation/index.tsx | 4 +-- package-lock.json | 4 +-- package.json | 2 +- src/container/EdgeRenderer/utils.ts | 12 +++++---- src/container/GraphView/index.tsx | 6 ++--- src/container/NodeRenderer/index.tsx | 4 +-- src/container/NodeRenderer/utils.ts | 12 +++++---- src/container/ReactFlow/index.tsx | 17 +++++++++--- src/container/ReactFlow/utils.ts | 28 ++++++++++++++++++++ src/types/component-props.ts | 8 +++--- src/types/general.ts | 4 +-- 17 files changed, 82 insertions(+), 40 deletions(-) create mode 100644 src/container/ReactFlow/utils.ts diff --git a/example/src/CustomNode/index.tsx b/example/src/CustomNode/index.tsx index 5b1f883b..8169dc52 100644 --- a/example/src/CustomNode/index.tsx +++ b/example/src/CustomNode/index.tsx @@ -27,6 +27,7 @@ const initBgColor = '#1A192B'; const connectionLineStyle = { stroke: '#fff' }; const snapGrid: SnapGrid = [16, 16]; + const nodeTypes = { selectorNode: ColorSelectorNode, }; diff --git a/example/src/Edges/index.tsx b/example/src/Edges/index.tsx index 50ea0157..d6e0a83d 100644 --- a/example/src/Edges/index.tsx +++ b/example/src/Edges/index.tsx @@ -6,7 +6,7 @@ import ReactFlow, { Controls, Background, ReactFlowInstance, - EdgeTypesType, + EdgeTypes, Connection, Edge, MarkerType, @@ -102,7 +102,7 @@ const initialEdges: Edge[] = [ }, ]; -const edgeTypes: EdgeTypesType = { +const edgeTypes: EdgeTypes = { custom: CustomEdge, custom2: CustomEdge2, }; diff --git a/example/src/FloatingEdges/index.tsx b/example/src/FloatingEdges/index.tsx index 7c1687a0..5a5fddbf 100644 --- a/example/src/FloatingEdges/index.tsx +++ b/example/src/FloatingEdges/index.tsx @@ -4,7 +4,7 @@ import ReactFlow, { addEdge, Background, ReactFlowInstance, - EdgeTypesType, + EdgeTypes, Connection, useNodesState, useEdgesState, @@ -20,7 +20,7 @@ const onInit = (reactFlowInstance: ReactFlowInstance) => reactFlowInstance.fitVi const { nodes: initialNodes, edges: initialEdges } = createElements(); -const edgeTypes: EdgeTypesType = { +const edgeTypes: EdgeTypes = { floating: FloatingEdge, }; diff --git a/example/src/NodeTypesObjectChange/index.tsx b/example/src/NodeTypesObjectChange/index.tsx index 1240ce7e..dda1bccc 100644 --- a/example/src/NodeTypesObjectChange/index.tsx +++ b/example/src/NodeTypesObjectChange/index.tsx @@ -7,7 +7,7 @@ import ReactFlow, { Connection, Edge, NodeProps, - NodeTypesType, + NodeTypes, useNodesState, useEdgesState, } from 'react-flow-renderer'; @@ -43,7 +43,7 @@ const NodeB: FC = () => { }; type NodeTypesObject = { - [key: string]: NodeTypesType; + [key: string]: NodeTypes; }; const nodeTypesObjects: NodeTypesObject = { diff --git a/example/src/Undirectional/index.tsx b/example/src/Undirectional/index.tsx index 26ad5ad6..5f955f08 100644 --- a/example/src/Undirectional/index.tsx +++ b/example/src/Undirectional/index.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react'; import ReactFlow, { useReactFlow, - NodeTypesType, + NodeTypes, addEdge, ReactFlowProvider, Node, @@ -172,7 +172,7 @@ const initialEdges: Edge[] = [ }, ]; -const nodeTypes: NodeTypesType = { +const nodeTypes: NodeTypes = { custom: CustomNode, }; diff --git a/example/src/UseUpdateNodeInternals/index.tsx b/example/src/UseUpdateNodeInternals/index.tsx index 3fc6555f..c5b200a0 100644 --- a/example/src/UseUpdateNodeInternals/index.tsx +++ b/example/src/UseUpdateNodeInternals/index.tsx @@ -1,7 +1,7 @@ import { useCallback, CSSProperties } from 'react'; import ReactFlow, { - NodeTypesType, + NodeTypes, addEdge, useReactFlow, ReactFlowProvider, @@ -28,7 +28,7 @@ const initialNodes: Node[] = [ const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 10 }; -const nodeTypes: NodeTypesType = { +const nodeTypes: NodeTypes = { custom: CustomNode, }; diff --git a/example/src/Validation/index.tsx b/example/src/Validation/index.tsx index 247302c5..ba58b0b5 100644 --- a/example/src/Validation/index.tsx +++ b/example/src/Validation/index.tsx @@ -8,7 +8,7 @@ import ReactFlow, { Edge, OnConnectStartParams, NodeProps, - NodeTypesType, + NodeTypes, useNodesState, useEdgesState, } from 'react-flow-renderer'; @@ -43,7 +43,7 @@ const CustomNode: FC = ({ id }) => ( ); -const nodeTypes: NodeTypesType = { +const nodeTypes: NodeTypes = { custominput: CustomInput, customnode: CustomNode, }; diff --git a/package-lock.json b/package-lock.json index 23d4e938..f03378e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-flow-renderer", - "version": "10.0.0-next.41", + "version": "10.0.0-next.42", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "react-flow-renderer", - "version": "10.0.0-next.41", + "version": "10.0.0-next.42", "license": "MIT", "dependencies": { "@babel/runtime": "^7.16.7", diff --git a/package.json b/package.json index a89f1a71..51f2d283 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-flow-renderer", - "version": "10.0.0-next.41", + "version": "10.0.0-next.42", "engines": { "node": ">=12" }, diff --git a/src/container/EdgeRenderer/utils.ts b/src/container/EdgeRenderer/utils.ts index 01292e6b..dac372ee 100644 --- a/src/container/EdgeRenderer/utils.ts +++ b/src/container/EdgeRenderer/utils.ts @@ -5,7 +5,7 @@ import wrapEdge from '../../components/Edges/wrapEdge'; import { rectToBox } from '../../utils'; import { - EdgeTypesType, + EdgeTypes, EdgeProps, HandleElement, Position, @@ -16,16 +16,18 @@ import { NodeHandleBounds, } from '../../types'; -export function createEdgeTypes(edgeTypes: EdgeTypesType): EdgeTypesType { - const standardTypes: EdgeTypesType = { +export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypes; + +export function createEdgeTypes(edgeTypes: EdgeTypes): EdgeTypes { + const standardTypes: EdgeTypes = { default: wrapEdge((edgeTypes.default || BezierEdge) as ComponentType), straight: wrapEdge((edgeTypes.bezier || StraightEdge) as ComponentType), step: wrapEdge((edgeTypes.step || StepEdge) as ComponentType), smoothstep: wrapEdge((edgeTypes.step || SmoothStepEdge) as ComponentType), }; - const wrappedTypes = {} as EdgeTypesType; - const specialTypes: EdgeTypesType = Object.keys(edgeTypes) + const wrappedTypes = {} as EdgeTypes; + const specialTypes: EdgeTypes = Object.keys(edgeTypes) .filter((k) => !['default', 'bezier'].includes(k)) .reduce((res, key) => { res[key] = wrapEdge((edgeTypes[key] || BezierEdge) as ComponentType); diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index c7e890a1..79c69e62 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -5,11 +5,11 @@ import NodeRenderer from '../NodeRenderer'; import EdgeRenderer from '../EdgeRenderer'; import Viewport from '../Viewport'; import useOnInitHandler from '../../hooks/useOnInitHandler'; -import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode, ReactFlowProps } from '../../types'; +import { NodeTypes, EdgeTypes, ConnectionLineType, KeyCode, ReactFlowProps } from '../../types'; export interface GraphViewProps extends Omit { - nodeTypes: NodeTypesType; - edgeTypes: EdgeTypesType; + nodeTypes: NodeTypes; + edgeTypes: EdgeTypes; selectionKeyCode: KeyCode; deleteKeyCode: KeyCode; multiSelectionKeyCode: KeyCode; diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 19078f1b..663458d3 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -3,10 +3,10 @@ import shallow from 'zustand/shallow'; import useVisibleNodes from '../../hooks/useVisibleNodes'; import { useStore } from '../../store'; -import { Node, NodeTypesType, Position, ReactFlowState, WrapNodeProps } from '../../types'; +import { Node, NodeTypes, Position, ReactFlowState, WrapNodeProps } from '../../types'; interface NodeRendererProps { - nodeTypes: NodeTypesType; + nodeTypes: NodeTypes; selectNodesOnDrag: boolean; onNodeClick?: (event: MouseEvent, element: Node) => void; onNodeDoubleClick?: (event: MouseEvent, element: Node) => void; diff --git a/src/container/NodeRenderer/utils.ts b/src/container/NodeRenderer/utils.ts index 74464f5c..c656d74a 100644 --- a/src/container/NodeRenderer/utils.ts +++ b/src/container/NodeRenderer/utils.ts @@ -5,18 +5,20 @@ import InputNode from '../../components/Nodes/InputNode'; import OutputNode from '../../components/Nodes/OutputNode'; import GroupNode from '../../components/Nodes/GroupNode'; import wrapNode from '../../components/Nodes/wrapNode'; -import { NodeTypesType, NodeProps } from '../../types'; +import { NodeTypes, NodeProps } from '../../types'; -export function createNodeTypes(nodeTypes: NodeTypesType): NodeTypesType { - const standardTypes: NodeTypesType = { +export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypes; + +export function createNodeTypes(nodeTypes: NodeTypes): NodeTypes { + const standardTypes: NodeTypes = { input: wrapNode((nodeTypes.input || InputNode) as ComponentType), default: wrapNode((nodeTypes.default || DefaultNode) as ComponentType), output: wrapNode((nodeTypes.output || OutputNode) as ComponentType), group: wrapNode((nodeTypes.group || GroupNode) as ComponentType), }; - const wrappedTypes = {} as NodeTypesType; - const specialTypes: NodeTypesType = Object.keys(nodeTypes) + const wrappedTypes = {} as NodeTypes; + const specialTypes: NodeTypes = Object.keys(nodeTypes) .filter((k) => !['input', 'default', 'output', 'group'].includes(k)) .reduce((res, key) => { res[key] = wrapNode((nodeTypes[key] || DefaultNode) as ComponentType); diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index 518858a6..3514e16c 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, forwardRef } from 'react'; +import React, { forwardRef } from 'react'; import cc from 'classcat'; import GraphView from '../GraphView'; @@ -13,10 +13,19 @@ import Attribution from '../../components/Attribution'; import { createEdgeTypes } from '../EdgeRenderer/utils'; import Wrapper from './Wrapper'; -import { ConnectionMode, ConnectionLineType, PanOnScrollMode, ReactFlowProps, ReactFlowRefType } from '../../types'; +import { + ConnectionMode, + ConnectionLineType, + PanOnScrollMode, + ReactFlowProps, + ReactFlowRefType, + NodeTypes, + EdgeTypes, +} from '../../types'; import '../../style.css'; import '../../theme-default.css'; +import { useNodeOrEdgeTypes } from './utils'; const defaultNodeTypes = { input: InputNode, @@ -127,8 +136,8 @@ const ReactFlow = forwardRef( }, ref ) => { - const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), [nodeTypes]); - const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), [edgeTypes]); + const nodeTypesParsed = useNodeOrEdgeTypes(nodeTypes, createNodeTypes) as NodeTypes; + const edgeTypesParsed = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes) as EdgeTypes; const reactFlowClasses = cc(['react-flow', className]); return ( diff --git a/src/container/ReactFlow/utils.ts b/src/container/ReactFlow/utils.ts new file mode 100644 index 00000000..ed088097 --- /dev/null +++ b/src/container/ReactFlow/utils.ts @@ -0,0 +1,28 @@ +import { useMemo, useRef } from 'react'; +import shallow from 'zustand/shallow'; + +import { EdgeTypes, NodeTypes } from '../../types'; +import { CreateEdgeTypes } from '../EdgeRenderer/utils'; +import { CreateNodeTypes } from '../NodeRenderer/utils'; + +export function useNodeOrEdgeTypes(nodeOrEdgeTypes: NodeTypes, createTypes: CreateNodeTypes): NodeTypes; +export function useNodeOrEdgeTypes(nodeOrEdgeTypes: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypes; +export function useNodeOrEdgeTypes(nodeOrEdgeTypes: any, createTypes: any): any { + const typesKeysRef = useRef(null); + + const typesParsed = useMemo(() => { + if (process.env.NODE_ENV === 'development') { + const typeKeys = Object.keys(nodeOrEdgeTypes); + if (shallow(typesKeysRef.current, typeKeys)) { + console.warn( + "React Flow: It looks like that you created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them." + ); + } + + typesKeysRef.current = typeKeys; + } + return createTypes(nodeOrEdgeTypes); + }, [nodeOrEdgeTypes]); + + return typesParsed; +} diff --git a/src/types/component-props.ts b/src/types/component-props.ts index bf79f696..f2e658bb 100644 --- a/src/types/component-props.ts +++ b/src/types/component-props.ts @@ -2,8 +2,8 @@ import React, { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, Wh import { OnSelectionChangeFunc, - NodeTypesType, - EdgeTypesType, + NodeTypes, + EdgeTypes, Node, Edge, ConnectionMode, @@ -68,8 +68,8 @@ export interface ReactFlowProps extends HTMLAttributes { onPaneScroll?: (event?: WheelEvent) => void; onPaneClick?: (event: ReactMouseEvent) => void; onPaneContextMenu?: (event: ReactMouseEvent) => void; - nodeTypes?: NodeTypesType; - edgeTypes?: EdgeTypesType; + nodeTypes?: NodeTypes; + edgeTypes?: EdgeTypes; connectionMode?: ConnectionMode; connectionLineType?: ConnectionLineType; connectionLineStyle?: CSSProperties; diff --git a/src/types/general.ts b/src/types/general.ts index 89d4a8c2..70bafaa9 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -9,8 +9,8 @@ import { HandleType, StartHandle } from './handles'; import { DefaultEdgeOptions } from '.'; import { ReactFlowInstance } from './instance'; -export type NodeTypesType = { [key: string]: ReactNode }; -export type EdgeTypesType = NodeTypesType; +export type NodeTypes = { [key: string]: ReactNode }; +export type EdgeTypes = NodeTypes; export type FitView = (fitViewOptions?: FitViewOptions) => void;