mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-28 20:40:06 +00:00
Dashboard performance improvements (#1671)
* dashboard: reduce rerendering of FileItems * dashboard: move pure HOC to util file * dashboard: memoize arrays passed to child elements too * dashboard: fix memoize-one import when bundler used `module` key
This commit is contained in:
parent
1d8ee14ead
commit
fcfdf64d7e
6 changed files with 115 additions and 47 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -14652,6 +14652,11 @@
|
|||
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
|
||||
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
|
||||
},
|
||||
"is-shallow-equal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz",
|
||||
"integrity": "sha512-lq5RvK+85Hs5J3p4oA4256M1FEffzmI533ikeDHvJd42nouRRx5wBzt36JuviiGe5dIPyHON/d0/Up+PBo6XkQ=="
|
||||
},
|
||||
"is-ssh": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz",
|
||||
|
|
@ -18771,6 +18776,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz",
|
||||
"integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA=="
|
||||
},
|
||||
"memorystream": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@
|
|||
"@uppy/utils": "1.1.0",
|
||||
"classnames": "^2.2.6",
|
||||
"cuid": "^2.1.1",
|
||||
"is-shallow-equal": "^1.0.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"memoize-one": "^5.0.4",
|
||||
"preact": "8.2.9",
|
||||
"preact-css-transition-group": "^1.3.0",
|
||||
"prettier-bytes": "^1.0.4",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
const { h } = require('preact')
|
||||
const classNames = require('classnames')
|
||||
|
||||
const pure = require('../../utils/pure')
|
||||
const FilePreviewAndLink = require('./FilePreviewAndLink')
|
||||
const FileProgress = require('./FileProgress')
|
||||
const FileInfo = require('./FileInfo')
|
||||
const Buttons = require('./Buttons')
|
||||
|
||||
module.exports = function FileItem (props) {
|
||||
module.exports = pure(function FileItem (props) {
|
||||
const file = props.file
|
||||
|
||||
const isProcessing = file.progress.preprocess || file.progress.postprocess
|
||||
|
|
@ -72,4 +72,4 @@ module.exports = function FileItem (props) {
|
|||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,32 @@ module.exports = (props) => {
|
|||
{ 'uppy-Dashboard-files--noFiles': noFiles }
|
||||
)
|
||||
|
||||
const fileProps = {
|
||||
// FIXME This is confusing, it's actually the Dashboard's plugin ID
|
||||
id: props.id,
|
||||
error: props.error,
|
||||
// TODO move this to context
|
||||
i18n: props.i18n,
|
||||
log: props.log,
|
||||
info: props.info,
|
||||
// features
|
||||
acquirers: props.acquirers,
|
||||
resumableUploads: props.resumableUploads,
|
||||
individualCancellation: props.individualCancellation,
|
||||
// visual options
|
||||
hideRetryButton: props.hideRetryButton,
|
||||
hidePauseResumeCancelButtons: props.hidePauseResumeCancelButtons,
|
||||
showLinkToFileUploadResult: props.showLinkToFileUploadResult,
|
||||
isWide: props.isWide,
|
||||
metaFields: props.metaFields,
|
||||
// callbacks
|
||||
retryUpload: props.retryUpload,
|
||||
pauseUpload: props.pauseUpload,
|
||||
cancelUpload: props.cancelUpload,
|
||||
toggleFileCard: props.toggleFileCard,
|
||||
removeFile: props.removeFile
|
||||
}
|
||||
|
||||
return (
|
||||
<ul
|
||||
class={dashboardFilesClass}
|
||||
|
|
@ -16,7 +42,7 @@ module.exports = (props) => {
|
|||
tabindex="-1">
|
||||
{Object.keys(props.files).map((fileID) => (
|
||||
<FileItem
|
||||
{...props}
|
||||
{...fileProps}
|
||||
file={props.files[fileID]}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const cuid = require('cuid')
|
|||
const ResizeObserver = require('resize-observer-polyfill').default || require('resize-observer-polyfill')
|
||||
const { defaultPickerIcon } = require('./components/icons')
|
||||
const createSuperFocus = require('./utils/createSuperFocus')
|
||||
const memoize = require('memoize-one').default || require('memoize-one')
|
||||
|
||||
const TAB_KEY = 9
|
||||
const ESC_KEY = 27
|
||||
|
|
@ -659,6 +660,52 @@ module.exports = class Dashboard extends Plugin {
|
|||
this.superFocusOnEachUpdate()
|
||||
}
|
||||
|
||||
startUpload = (ev) => {
|
||||
this.uppy.upload().catch((err) => {
|
||||
// Log error.
|
||||
this.uppy.log(err.stack || err.message || err)
|
||||
})
|
||||
}
|
||||
|
||||
cancelUpload = (fileID) => {
|
||||
this.uppy.removeFile(fileID)
|
||||
}
|
||||
|
||||
saveFileCard = (meta, fileID) => {
|
||||
this.uppy.setFileMeta(fileID, meta)
|
||||
this.toggleFileCard()
|
||||
}
|
||||
|
||||
_attachRenderFunctionToTarget = (target) => {
|
||||
const plugin = this.uppy.getPlugin(target.id)
|
||||
return {
|
||||
...target,
|
||||
icon: plugin.icon || this.opts.defaultPickerIcon,
|
||||
render: plugin.render
|
||||
}
|
||||
}
|
||||
|
||||
_isTargetSupported = (target) => {
|
||||
const plugin = this.uppy.getPlugin(target.id)
|
||||
// If the plugin does not provide a `supported` check, assume the plugin works everywhere.
|
||||
if (typeof plugin.isSupported !== 'function') {
|
||||
return true
|
||||
}
|
||||
return plugin.isSupported()
|
||||
}
|
||||
|
||||
_getAcquirers = memoize((targets) => {
|
||||
return targets
|
||||
.filter(target => target.type === 'acquirer' && this._isTargetSupported(target))
|
||||
.map(this._attachRenderFunctionToTarget)
|
||||
})
|
||||
|
||||
_getProgressIndicators = memoize((targets) => {
|
||||
return targets
|
||||
.filter(target => target.type === 'progressindicator')
|
||||
.map(this._attachRenderFunctionToTarget)
|
||||
})
|
||||
|
||||
render (state) {
|
||||
const pluginState = this.getPluginState()
|
||||
const { files, capabilities, allowNewUpload } = state
|
||||
|
|
@ -710,46 +757,8 @@ module.exports = class Dashboard extends Plugin {
|
|||
const isAllPaused = inProgressFiles.length !== 0 &&
|
||||
pausedFiles.length === inProgressFiles.length
|
||||
|
||||
const attachRenderFunctionToTarget = (target) => {
|
||||
const plugin = this.uppy.getPlugin(target.id)
|
||||
return Object.assign({}, target, {
|
||||
icon: plugin.icon || this.opts.defaultPickerIcon,
|
||||
render: plugin.render
|
||||
})
|
||||
}
|
||||
|
||||
const isSupported = (target) => {
|
||||
const plugin = this.uppy.getPlugin(target.id)
|
||||
// If the plugin does not provide a `supported` check, assume the plugin works everywhere.
|
||||
if (typeof plugin.isSupported !== 'function') {
|
||||
return true
|
||||
}
|
||||
return plugin.isSupported()
|
||||
}
|
||||
|
||||
const acquirers = pluginState.targets
|
||||
.filter(target => target.type === 'acquirer' && isSupported(target))
|
||||
.map(attachRenderFunctionToTarget)
|
||||
|
||||
const progressindicators = pluginState.targets
|
||||
.filter(target => target.type === 'progressindicator')
|
||||
.map(attachRenderFunctionToTarget)
|
||||
|
||||
const startUpload = (ev) => {
|
||||
this.uppy.upload().catch((err) => {
|
||||
// Log error.
|
||||
this.uppy.log(err.stack || err.message || err)
|
||||
})
|
||||
}
|
||||
|
||||
const cancelUpload = (fileID) => {
|
||||
this.uppy.removeFile(fileID)
|
||||
}
|
||||
|
||||
const saveFileCard = (meta, fileID) => {
|
||||
this.uppy.setFileMeta(fileID, meta)
|
||||
this.toggleFileCard()
|
||||
}
|
||||
const acquirers = this._getAcquirers(pluginState.targets)
|
||||
const progressindicators = this._getProgressIndicators(pluginState.targets)
|
||||
|
||||
return DashboardUI({
|
||||
state,
|
||||
|
|
@ -794,16 +803,16 @@ module.exports = class Dashboard extends Plugin {
|
|||
metaFields: pluginState.metaFields,
|
||||
resumableUploads: capabilities.resumableUploads || false,
|
||||
individualCancellation: capabilities.individualCancellation,
|
||||
startUpload,
|
||||
startUpload: this.startUpload,
|
||||
pauseUpload: this.uppy.pauseResume,
|
||||
retryUpload: this.uppy.retryUpload,
|
||||
cancelUpload,
|
||||
cancelUpload: this.cancelUpload,
|
||||
cancelAll: this.uppy.cancelAll,
|
||||
fileCardFor: pluginState.fileCardFor,
|
||||
toggleFileCard: this.toggleFileCard,
|
||||
toggleAddFilesPanel: this.toggleAddFilesPanel,
|
||||
showAddFilesPanel: pluginState.showAddFilesPanel,
|
||||
saveFileCard,
|
||||
saveFileCard: this.saveFileCard,
|
||||
width: this.opts.width,
|
||||
height: this.opts.height,
|
||||
showLinkToFileUploadResult: this.opts.showLinkToFileUploadResult,
|
||||
|
|
|
|||
21
packages/@uppy/dashboard/src/utils/pure.js
Normal file
21
packages/@uppy/dashboard/src/utils/pure.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const shallowEqual = require('is-shallow-equal')
|
||||
const { h, Component } = require('preact')
|
||||
|
||||
/**
|
||||
* Higher order component that doesn't rerender an element if its props didn't change.
|
||||
*/
|
||||
module.exports = function pure (Inner) {
|
||||
return class Pure extends Component {
|
||||
shouldComponentUpdate (nextProps) {
|
||||
return !shallowEqual(this.props, nextProps)
|
||||
}
|
||||
|
||||
render () {
|
||||
// we have to clone this or Preact mutates it:
|
||||
// https://github.com/preactjs/preact/issues/836
|
||||
// TODO can be removed if we upgrade to Preact X
|
||||
const props = { ...this.props }
|
||||
return <Inner {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue