From 5206dc4ef1a6a2a4e8db6b2b6635e585e6ebb92a Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 14 Dec 2018 19:12:42 -0500 Subject: [PATCH] Reduce updates caused by typing (#604) * prevent index page from updating * implement shouldComponentUpdate for ExportMenu * replace componentDidUpdate with updateState in Editor * replace shallowCompare with React.memo * add flex-basis to prevent jank --- components/Editor.js | 40 ++++++++++++---------------- components/ExportMenu.js | 56 ++++++++++++++++++++++++++++++---------- pages/index.js | 2 ++ 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/components/Editor.js b/components/Editor.js index 094e262..58f27a2 100644 --- a/components/Editor.js +++ b/components/Editor.js @@ -6,7 +6,6 @@ import { DragDropContext } from 'react-dnd' import domtoimage from 'dom-to-image' import ReadFileDropContainer, { DATA_URL, TEXT } from 'dropperx' import Spinner from 'react-spinner' -import shallowCompare from 'react-addons-shallow-compare' // Ours import Button from './Button' @@ -102,7 +101,7 @@ class Editor extends React.Component { newState.language = unescapeHtml(newState.language) } - this.setState(newState) + this.updateState(newState) window.addEventListener('offline', this.setOffline) window.addEventListener('online', this.setOnline) @@ -113,18 +112,13 @@ class Editor extends React.Component { window.removeEventListener('online', this.setOnline) } - componentDidUpdate(prevProps, prevState) { - // this.props ensures props are not compared, only state - if (shallowCompare(this, this.props, prevState)) { - this.props.onUpdate(this.state) - } - } + updateState = updates => this.setState(updates, () => this.props.onUpdate(this.state)) - updateCode = code => this.setState({ code }) - updateAspectRatio = aspectRatio => this.setState({ aspectRatio }) - updateTitleBar = titleBar => this.setState({ titleBar }) - setOffline = () => this.setState({ online: false }) - setOnline = () => this.setState({ online: true }) + updateCode = code => this.updateState({ code }) + updateAspectRatio = aspectRatio => this.updateState({ aspectRatio }) + updateTitleBar = titleBar => this.updateState({ titleBar }) + setOffline = () => this.updateState({ online: false }) + setOnline = () => this.updateState({ online: true }) async getCarbonImage( { @@ -208,9 +202,9 @@ class Editor extends React.Component { } updateSetting(key, value) { - this.setState({ [key]: value }) + this.updateState({ [key]: value }) if (Object.prototype.hasOwnProperty.call(DEFAULT_SETTINGS, key)) { - this.setState({ preset: null }) + this.updateState({ preset: null }) } } @@ -231,29 +225,29 @@ class Editor extends React.Component { } resetDefaultSettings() { - this.setState({ ...DEFAULT_SETTINGS, preset: DEFAULT_PRESET_ID }) + this.updateState({ ...DEFAULT_SETTINGS, preset: DEFAULT_PRESET_ID }) this.props.onReset() } upload() { - this.setState({ uploading: true }) + this.updateState({ uploading: true }) this.getCarbonImage({ format: 'png' }) .then(this.props.api.tweet.bind(null, this.state.code || DEFAULT_CODE)) // eslint-disable-next-line .catch(console.error) - .then(() => this.setState({ uploading: false })) + .then(() => this.updateState({ uploading: false })) } onDrop([file]) { if (isImage(file)) { - this.setState({ + this.updateState({ backgroundImage: file.content, backgroundImageSelection: null, backgroundMode: 'image', preset: null }) } else { - this.setState({ code: file.content, language: 'auto' }) + this.updateState({ code: file.content, language: 'auto' }) } } @@ -267,13 +261,13 @@ class Editor extends React.Component { updateBackground({ photographer, ...changes } = {}) { if (photographer) { - this.setState(({ code = DEFAULT_CODE }) => ({ + this.updateState(({ code = DEFAULT_CODE }) => ({ ...changes, code: code + `\n\n// Photo by ${photographer.name} on Unsplash`, preset: null })) } else { - this.setState({ ...changes, preset: null }) + this.updateState({ ...changes, preset: null }) } } @@ -284,7 +278,7 @@ class Editor extends React.Component { // create toast here in the future }) - applyPreset = ({ id: preset, ...settings }) => this.setState({ preset, ...settings }) + applyPreset = ({ id: preset, ...settings }) => this.updateState({ preset, ...settings }) render() { const { diff --git a/components/ExportMenu.js b/components/ExportMenu.js index 46676f3..1d9ab47 100644 --- a/components/ExportMenu.js +++ b/components/ExportMenu.js @@ -17,6 +17,46 @@ const toIFrame = url => ` +const CopyEmbed = withRouter( + React.memo( + ({ router: { asPath } }) => ( + + + {({ copied }) => ( + + )} + + + + ), + (prevProps, nextProps) => prevProps.router.asPath === nextProps.router.asPath + ) +) + class ExportMenu extends React.PureComponent { state = { isVisible: false @@ -33,7 +73,7 @@ class ExportMenu extends React.PureComponent { handleExport = format => () => this.props.export(format) render() { - const { exportSize, filename, router } = this.props + const { exportSize, filename } = this.props const { isVisible } = this.state return ( @@ -77,11 +117,7 @@ class ExportMenu extends React.PureComponent { - - {({ copied }) => ( - - )} - +
Save as
@@ -187,7 +223,6 @@ class ExportMenu extends React.PureComponent { opacity: 1; } - .copy-button, .open-button, .save-container { display: flex; @@ -197,11 +232,6 @@ class ExportMenu extends React.PureComponent { padding: 12px 16px; } - .copy-button { - white-space: nowrap; - } - - .copy-button, .open-button { border-right: 1px solid ${COLORS.PURPLE}; } @@ -229,4 +259,4 @@ class ExportMenu extends React.PureComponent { } } -export default withRouter(enhanceWithClickOutside(ExportMenu)) +export default enhanceWithClickOutside(ExportMenu) diff --git a/pages/index.js b/pages/index.js index b379010..443fcd9 100644 --- a/pages/index.js +++ b/pages/index.js @@ -10,6 +10,8 @@ import { updateQueryString } from '../lib/routing' import { saveSettings, clearSettings, omit } from '../lib/util' class Index extends React.Component { + shouldComponentUpdate = () => false + onEditorUpdate = state => { updateQueryString(this.props.router, state) saveSettings(