refactor(nodes): only re render changed nodes
This commit is contained in:
Vendored
+1906
-1893
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@ const SpecialNode = ({ data, styles }) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onNodeDragStop = node => console.log('drag stop', node);
|
||||||
|
|
||||||
class App extends PureComponent {
|
class App extends PureComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -58,6 +60,9 @@ class App extends PureComponent {
|
|||||||
{ source: '6', target: '7', style: { stroke: '#FFCC00' }},
|
{ source: '6', target: '7', style: { stroke: '#FFCC00' }},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.onElementClick = this.onElementClick.bind(this);
|
||||||
|
this.onConnect = this.onConnect.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(graphInstance) {
|
onLoad(graphInstance) {
|
||||||
@@ -123,10 +128,10 @@ class App extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<Graph
|
<Graph
|
||||||
elements={this.state.elements}
|
elements={this.state.elements}
|
||||||
onElementClick={element => this.onElementClick(element)}
|
onElementClick={this.onElementClick}
|
||||||
onElementsRemove={elements => this.onElementsRemove(elements)}
|
onElementsRemove={elements => this.onElementsRemove(elements)}
|
||||||
onConnect={params => this.onConnect(params)}
|
onConnect={this.onConnect}
|
||||||
onNodeDragStop={node => console.log('drag stop', node)}
|
onNodeDragStop={onNodeDragStop}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
onLoad={graphInstance => this.onLoad(graphInstance)}
|
onLoad={graphInstance => this.onLoad(graphInstance)}
|
||||||
onChange={(elements) => this.onChange(elements)}
|
onChange={(elements) => this.onChange(elements)}
|
||||||
|
|||||||
+1913
-1886
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -6,29 +6,36 @@ import { isEdge } from '../../graph-utils';
|
|||||||
|
|
||||||
const isInput = e => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
const isInput = e => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||||
|
|
||||||
export default EdgeComponent => memo((props) => {
|
export default EdgeComponent => {
|
||||||
const {
|
const WrappedEdge = memo((props) => {
|
||||||
source, target, animated, type,
|
const {
|
||||||
dispatch, selectedElements, onClick
|
source, target, animated, type,
|
||||||
} = props;
|
dispatch, selectedElements, onClick
|
||||||
const selected = selectedElements
|
} = props;
|
||||||
.filter(e => isEdge(e))
|
const selected = selectedElements
|
||||||
.find(e => e.source === source && e.target === target);
|
.filter(e => isEdge(e))
|
||||||
const edgeClasses = cx('react-graph__edge', { selected, animated: animated });
|
.find(e => e.source === source && e.target === target);
|
||||||
|
const edgeClasses = cx('react-graph__edge', { selected, animated: animated });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<g
|
||||||
className={edgeClasses}
|
className={edgeClasses}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (isInput(e)) {
|
if (isInput(e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(setSelectedElements({ source, target }));
|
dispatch(setSelectedElements({ source, target }));
|
||||||
onClick({ source, target, type });
|
onClick({ source, target, type });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EdgeComponent {...props} />
|
<EdgeComponent {...props} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
WrappedEdge.displayName = 'Wrapped Edge';
|
||||||
|
WrappedEdge.whyDidYouRender = false;
|
||||||
|
|
||||||
|
return WrappedEdge;
|
||||||
|
};
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ export const Provider = (props) => {
|
|||||||
const existingNode = state.nodes.find(n => n.id === propNode.id);
|
const existingNode = state.nodes.find(n => n.id === propNode.id);
|
||||||
|
|
||||||
if (existingNode) {
|
if (existingNode) {
|
||||||
|
const data = !isEqual(existingNode.data, propNode.data) ?
|
||||||
|
{ ...existingNode.data, ...propNode.data } : existingNode.data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...existingNode,
|
...existingNode,
|
||||||
data: { ...existingNode.data, ...propNode.data }
|
data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,52 +76,56 @@ const onStop = ({ onNodeDragStop, id, type, position, data }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NodeComponent => memo((props) => {
|
export default NodeComponent => {
|
||||||
const nodeElement = useRef(null);
|
const WrappedComp = memo((props) => {
|
||||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
const nodeElement = useRef(null);
|
||||||
const {
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||||
id, type, data, transform, xPos, yPos, selectedElements,
|
const {
|
||||||
dispatch, getNodeById, onClick, onNodeDragStop
|
id, type, data, transform, xPos, yPos, selectedElements,
|
||||||
} = props;
|
dispatch, getNodeById, onClick, onNodeDragStop
|
||||||
|
} = props;
|
||||||
|
|
||||||
console.log('render node', id);
|
const position = { x: xPos, y: yPos };
|
||||||
|
const [ x, y, k ] = transform;
|
||||||
|
const selected = selectedElements.filter(isNode).map(e => e.id).includes(id);
|
||||||
|
const nodeClasses = cx('react-graph__node', { selected });
|
||||||
|
const nodeStyle = { zIndex: selected ? 10 : 3, transform: `translate(${xPos}px,${yPos}px)` };
|
||||||
|
|
||||||
const position = { x: xPos, y: yPos };
|
useEffect(() => {
|
||||||
const [ x, y, k ] = transform;
|
const bounds = nodeElement.current.getBoundingClientRect();
|
||||||
const selected = selectedElements.filter(isNode).map(e => e.id).includes(id);
|
const unscaledWith = Math.round(bounds.width * (1 / k));
|
||||||
const nodeClasses = cx('react-graph__node', { selected });
|
const unscaledHeight = Math.round(bounds.height * (1 / k));
|
||||||
const nodeStyle = { zIndex: selected ? 10 : 3, transform: `translate(${xPos}px,${yPos}px)` };
|
const handleBounds = {
|
||||||
|
source: getHandleBounds('.source', nodeElement.current, bounds, k),
|
||||||
|
target: getHandleBounds('.target', nodeElement.current, bounds, k)
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight, handleBounds }));
|
||||||
const bounds = nodeElement.current.getBoundingClientRect();
|
}, []);
|
||||||
const unscaledWith = Math.round(bounds.width * (1 / k));
|
|
||||||
const unscaledHeight = Math.round(bounds.height * (1 / k));
|
|
||||||
const handleBounds = {
|
|
||||||
source: getHandleBounds('.source', nodeElement.current, bounds, k),
|
|
||||||
target: getHandleBounds('.target', nodeElement.current, bounds, k)
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight, handleBounds }));
|
return (
|
||||||
}, []);
|
<ReactDraggable.DraggableCore
|
||||||
|
onStart={evt => onStart(evt, { setOffset, transform, position })}
|
||||||
return (
|
onDrag={evt => onDrag(evt, { dispatch, id, offset, transform })}
|
||||||
<ReactDraggable.DraggableCore
|
onStop={() => onStop({ onNodeDragStop, id, type, position, data })}
|
||||||
onStart={evt => onStart(evt, { setOffset, transform, position })}
|
scale={transform[2]}
|
||||||
onDrag={evt => onDrag(evt, { dispatch, id, offset, transform })}
|
|
||||||
onStop={() => onStop({ onNodeDragStop, id, type, position, data })}
|
|
||||||
scale={transform[2]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={nodeClasses}
|
|
||||||
ref={nodeElement}
|
|
||||||
style={nodeStyle}
|
|
||||||
onClick={evt => onNodeClick(evt, { getNodeById, onClick, dispatch, id, type, position, data })}
|
|
||||||
>
|
>
|
||||||
<Provider value={id}>
|
<div
|
||||||
<NodeComponent {...props} selected={selected} />
|
className={nodeClasses}
|
||||||
</Provider>
|
ref={nodeElement}
|
||||||
</div>
|
style={nodeStyle}
|
||||||
</ReactDraggable.DraggableCore>
|
onClick={evt => onNodeClick(evt, { getNodeById, onClick, dispatch, id, type, position, data })}
|
||||||
);
|
>
|
||||||
}
|
<Provider value={id}>
|
||||||
);
|
<NodeComponent {...props} selected={selected} />
|
||||||
|
</Provider>
|
||||||
|
</div>
|
||||||
|
</ReactDraggable.DraggableCore>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
WrappedComp.displayName = 'Wrapped Node';
|
||||||
|
WrappedComp.whyDidYouRender = false;
|
||||||
|
|
||||||
|
return WrappedComp;
|
||||||
|
};
|
||||||
|
|||||||
+29
-9
@@ -1,11 +1,12 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import isEqual from 'lodash.isequal';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
||||||
whyDidYouRender(React);
|
whyDidYouRender(React);
|
||||||
}
|
}
|
||||||
|
|
||||||
import { parseElements, separateElements } from '../graph-utils';
|
import { parseElements, isNode, isEdge } from '../graph-utils';
|
||||||
import GraphView from '../GraphView';
|
import GraphView from '../GraphView';
|
||||||
import GlobalKeyHandler from '../GlobalKeyHandler';
|
import GlobalKeyHandler from '../GlobalKeyHandler';
|
||||||
import { Provider } from '../GraphContext';
|
import { Provider } from '../GraphContext';
|
||||||
@@ -27,23 +28,42 @@ class ReactGraph extends PureComponent {
|
|||||||
|
|
||||||
this.nodeTypes = createNodeTypes(props.nodeTypes);
|
this.nodeTypes = createNodeTypes(props.nodeTypes);
|
||||||
this.edgeTypes = createEdgeTypes(props.edgeTypes);
|
this.edgeTypes = createEdgeTypes(props.edgeTypes);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
nodes: [],
|
||||||
|
edges: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.updateElements(this.props.elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (!isEqual(prevProps.elements, this.props.elements)) {
|
||||||
|
this.updateElements(this.props.elements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateElements(elements) {
|
||||||
|
const parsedElements = elements.map(parseElements);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
nodes: parsedElements.filter(isNode),
|
||||||
|
edges: parsedElements.filter(isEdge),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
style, onElementClick, children, onLoad,
|
style, onElementClick, children, onLoad,
|
||||||
onMove, onChange, elements, onElementsRemove,
|
onMove, onChange, onElementsRemove, onConnect, onNodeDragStop,
|
||||||
onConnect, onNodeDragStop, connectionLineType,
|
connectionLineType, connectionLineStyle
|
||||||
connectionLineStyle
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { nodes, edges } = elements
|
|
||||||
.map(parseElements)
|
|
||||||
.reduce(separateElements, {});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style} className="react-graph">
|
<div style={style} className="react-graph">
|
||||||
<Provider nodes={nodes} edges={edges} onConnect={onConnect}>
|
<Provider nodes={this.state.nodes} edges={this.state.edges} onConnect={onConnect}>
|
||||||
<GraphView
|
<GraphView
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
onMove={onMove}
|
onMove={onMove}
|
||||||
|
|||||||
+10
-10
@@ -27,7 +27,7 @@ let internalNodeId = 0;
|
|||||||
|
|
||||||
const getId = () => internalNodeId++;
|
const getId = () => internalNodeId++;
|
||||||
|
|
||||||
export const parseElements = e => {
|
export const parseElements = (e) => {
|
||||||
e.type = e.type || 'default';
|
e.type = e.type || 'default';
|
||||||
e.id = e.id ? e.id : getId();
|
e.id = e.id ? e.id : getId();
|
||||||
|
|
||||||
@@ -35,15 +35,15 @@ export const parseElements = e => {
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
e.id = e.id.toString();
|
||||||
...e,
|
e.__rg = {
|
||||||
id: e.id.toString(),
|
position: e.position,
|
||||||
__rg: {
|
width: null,
|
||||||
position: e.position,
|
height: null,
|
||||||
width: null,
|
handleBounds : {}
|
||||||
height: null
|
};
|
||||||
}
|
|
||||||
}
|
return { ...e };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const separateElements = (res, element) => {
|
export const separateElements = (res, element) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user