From 8ab176b9a59b974798d06169d2e86a3e31aa33a5 Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Sun, 26 Jan 2020 19:08:46 +0000 Subject: [PATCH] Disable UI rendering when history is skipped (#574) When we are scrolling, resizing, or moving elements, we already disable the history. Since those actions do not change the state of the UI, we can also avoid re-drawing it and save ~10ms per frame. I had to change all the forceUpdate() to setState({}), otherwise it would bypass shouldComponentUpdate. --- src/index.tsx | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 59590e6a1..2360f2017 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -180,7 +180,7 @@ export class App extends React.Component { private syncActionResult = (res: ActionResult) => { if (res.elements !== undefined) { elements = res.elements; - this.forceUpdate(); + this.setState({}); } if (res.appState !== undefined) { @@ -199,7 +199,7 @@ export class App extends React.Component { ), ); elements = deleteSelectedElements(elements); - this.forceUpdate(); + this.setState({}); e.preventDefault(); }; private onCopy = (e: ClipboardEvent) => { @@ -226,6 +226,14 @@ export class App extends React.Component { this.saveDebounced.flush(); }; + public shouldComponentUpdate() { + if (!history.isRecording()) { + this.componentDidUpdate(); + return false; + } + return true; + } + public async componentDidMount() { document.addEventListener("copy", this.onCopy); document.addEventListener("paste", this.onPaste); @@ -253,7 +261,7 @@ export class App extends React.Component { if (data.appState) { this.setState(data.appState); } else { - this.forceUpdate(); + this.setState({}); } } @@ -275,7 +283,7 @@ export class App extends React.Component { public state: AppState = getDefaultAppState(); private onResize = () => { - this.forceUpdate(); + this.setState({}); }; private updateCurrentCursorPosition = (e: MouseEvent) => { @@ -286,7 +294,7 @@ export class App extends React.Component { private onKeyDown = (event: KeyboardEvent) => { if (event.key === KEYS.ESCAPE && !this.state.draggingElement) { elements = clearSelection(elements); - this.forceUpdate(); + this.setState({}); this.setState({ elementType: "selection" }); if (window.document.activeElement instanceof HTMLElement) { window.document.activeElement.blur(); @@ -320,7 +328,7 @@ export class App extends React.Component { } return el; }); - this.forceUpdate(); + this.setState({}); event.preventDefault(); } else if ( shapesShortcutKeys.includes(event.key.toLowerCase()) && @@ -505,7 +513,7 @@ export class App extends React.Component { elements = clearSelection(elements); document.documentElement.style.cursor = value === "text" ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR; - this.forceUpdate(); + this.setState({}); }} > ); @@ -695,7 +703,7 @@ export class App extends React.Component { if (!element.isSelected) { elements = clearSelection(elements); element.isSelected = true; - this.forceUpdate(); + this.setState({}); } ContextMenu.push({ @@ -737,6 +745,8 @@ export class App extends React.Component { let deltaY = lastY - e.clientY; lastX = e.clientX; lastY = e.clientY; + // We don't want to save history when panning around + history.skipRecording(); this.setState(state => ({ scrollX: state.scrollX - deltaX, scrollY: state.scrollY - deltaY, @@ -941,6 +951,8 @@ export class App extends React.Component { if (isOverHorizontalScrollBar) { const x = e.clientX - CANVAS_WINDOW_OFFSET_LEFT; const dx = x - lastX; + // We don't want to save history when scrolling + history.skipRecording(); this.setState(state => ({ scrollX: state.scrollX - dx })); lastX = x; return; @@ -949,6 +961,8 @@ export class App extends React.Component { if (isOverVerticalScrollBar) { const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP; const dy = y - lastY; + // We don't want to save history when scrolling + history.skipRecording(); this.setState(state => ({ scrollY: state.scrollY - dy })); lastY = y; return; @@ -1051,7 +1065,7 @@ export class App extends React.Component { lastY = y; // We don't want to save history when resizing an element history.skipRecording(); - this.forceUpdate(); + this.setState({}); return; } } @@ -1072,7 +1086,7 @@ export class App extends React.Component { lastY = y; // We don't want to save history when dragging an element to initially size it history.skipRecording(); - this.forceUpdate(); + this.setState({}); return; } } @@ -1127,7 +1141,7 @@ export class App extends React.Component { } // We don't want to save history when moving an element history.skipRecording(); - this.forceUpdate(); + this.setState({}); }; const onMouseUp = (e: MouseEvent) => { @@ -1152,12 +1166,11 @@ export class App extends React.Component { this.setState({ draggingElement: null, }); - this.forceUpdate(); return; } if (normalizeDimensions(draggingElement)) { - this.forceUpdate(); + this.setState({}); } if (resizingElement && isInvisiblySmallElement(resizingElement)) { @@ -1188,7 +1201,7 @@ export class App extends React.Component { if (draggingElement === null) { // if no element is clicked, clear the selection and redraw elements = clearSelection(elements); - this.forceUpdate(); + this.setState({}); return; } @@ -1208,7 +1221,7 @@ export class App extends React.Component { } history.resumeRecording(); - this.forceUpdate(); + this.setState({}); }; lastMouseUp = onMouseUp; @@ -1218,7 +1231,7 @@ export class App extends React.Component { // We don't want to save history on mouseDown, only on mouseUp when it's fully configured history.skipRecording(); - this.forceUpdate(); + this.setState({}); }} onDoubleClick={e => { const { x, y } = viewportCoordsToSceneCoords(e, this.state); @@ -1253,7 +1266,7 @@ export class App extends React.Component { elements = elements.filter( element => element.id !== elementAtPosition.id, ); - this.forceUpdate(); + this.setState({}); textX = this.state.scrollX + @@ -1355,6 +1368,8 @@ export class App extends React.Component { private handleWheel = (e: WheelEvent) => { e.preventDefault(); const { deltaX, deltaY } = e; + // We don't want to save history when panning around + history.skipRecording(); this.setState(state => ({ scrollX: state.scrollX - deltaX, scrollY: state.scrollY - deltaY, @@ -1412,7 +1427,7 @@ export class App extends React.Component { return duplicate; }), ]; - this.forceUpdate(); + this.setState({}); } };