소스 검색

react: Different way to do React components

This is a different approach to React components suggested by @arturi.
Instead of the components adding and removing plugins when mounting and
unmounting, plugins are configured at the start, and the components act
as slots for the plugins to actually mount in. Because all plugins are
still being configured up front here, we don't have to change how the
provider `target:` options work, they can install themselves into eg.
the Dashboard plugin immediately.

The core of this approach is the `UppyWrapper` component, which takes an
Uppy instance and a plugin ID, and mounts the plugin inside itself. The
other components only provide a default plugin ID.

The one change required in Uppy itself for this to work is to the
`mount()` functions: now, `mount()` configures `this.target` on the
plugins. If plugins do not have a `target:` option, they do not mount on
install. Some DOM code also had to be moved into the `mount()` function
for the DragDrop component instead of staying in `install()`, but I
think it makes sense.

(Still have to work out how to make the `DashboardModal` component's
`onRequestClose` option work.)
Renée Kooi 7 년 전
부모
커밋
d330c26813

+ 4 - 2
src/plugins/Dashboard/index.js

@@ -419,8 +419,10 @@ module.exports = class DashboardUI extends Plugin {
     }})
     }})
 
 
     const target = this.opts.target
     const target = this.opts.target
-    const plugin = this
-    this.target = this.mount(target, plugin)
+
+    if (target) {
+      this.mount(target, this)
+    }
 
 
     if (!this.opts.disableStatusBar) {
     if (!this.opts.disableStatusBar) {
       this.core.use(StatusBar, {
       this.core.use(StatusBar, {

+ 14 - 3
src/plugins/DragDrop/index.js

@@ -36,7 +36,7 @@ module.exports = class DragDrop extends Plugin {
 
 
     // Default options
     // Default options
     const defaultOpts = {
     const defaultOpts = {
-      target: '.UppyDragDrop',
+      target: null,
       getMetaFromForm: true,
       getMetaFromForm: true,
       locale: defaultLocale
       locale: defaultLocale
     }
     }
@@ -146,7 +146,13 @@ module.exports = class DragDrop extends Plugin {
   install () {
   install () {
     const target = this.opts.target
     const target = this.opts.target
     const plugin = this
     const plugin = this
-    this.target = this.mount(target, plugin)
+    if (target) {
+      this.mount(target, plugin)
+    }
+  }
+
+  mount (...args) {
+    super.mount(...args)
 
 
     const dndContainer = this.target.querySelector('.UppyDragDrop-container')
     const dndContainer = this.target.querySelector('.UppyDragDrop-container')
     this.removeDragDropListener = dragDrop(dndContainer, (files) => {
     this.removeDragDropListener = dragDrop(dndContainer, (files) => {
@@ -155,8 +161,13 @@ module.exports = class DragDrop extends Plugin {
     })
     })
   }
   }
 
 
-  uninstall () {
+  unmount (...args) {
     this.removeDragDropListener()
     this.removeDragDropListener()
+
+    super.unmount(...args)
+  }
+
+  uninstall () {
     this.unmount()
     this.unmount()
   }
   }
 }
 }

+ 20 - 16
src/plugins/Plugin.js

@@ -13,7 +13,6 @@ const getFormData = require('get-form-data')
  * @return {array | string} files or success/fail message
  * @return {array | string} files or success/fail message
  */
  */
 module.exports = class Plugin {
 module.exports = class Plugin {
-
   constructor (core, opts) {
   constructor (core, opts) {
     this.core = core
     this.core = core
     this.opts = opts || {}
     this.opts = opts || {}
@@ -72,34 +71,39 @@ module.exports = class Plugin {
       this.el = plugin.render(this.core.state)
       this.el = plugin.render(this.core.state)
       targetElement.appendChild(this.el)
       targetElement.appendChild(this.el)
 
 
+      this.target = targetElement
+
       return targetElement
       return targetElement
     }
     }
 
 
-    const Target = target
-    // Find the target plugin instance.
-    let targetPlugin
-    this.core.iteratePlugins((plugin) => {
-      if (plugin instanceof Target) {
-        targetPlugin = plugin
-        return false
+    if (typeof target === 'function') {
+      const Target = target
+      // Find the target plugin instance.
+      let targetPlugin
+      this.core.iteratePlugins((plugin) => {
+        if (plugin instanceof Target) {
+          targetPlugin = plugin
+          return false
+        }
+      })
+
+      if (targetPlugin) {
+        const targetPluginName = targetPlugin.id
+        this.core.log(`Installing ${callerPluginName} to ${targetPluginName}`)
+        this.target = targetPlugin
+        return targetPlugin.addTarget(plugin)
       }
       }
-    })
-
-    if (targetPlugin) {
-      const targetPluginName = targetPlugin.id
-      this.core.log(`Installing ${callerPluginName} to ${targetPluginName}`)
-      return targetPlugin.addTarget(plugin)
     }
     }
 
 
     this.core.log(`Not installing ${callerPluginName}`)
     this.core.log(`Not installing ${callerPluginName}`)
-
-    return null
+    throw new Error(`Invalid target option given to ${callerPluginName}`)
   }
   }
 
 
   unmount () {
   unmount () {
     if (this.el && this.el.parentNode) {
     if (this.el && this.el.parentNode) {
       this.el.parentNode.removeChild(this.el)
       this.el.parentNode.removeChild(this.el)
     }
     }
+    this.target = null
   }
   }
 
 
   install () {
   install () {

+ 3 - 1
src/plugins/ProgressBar.js

@@ -37,7 +37,9 @@ module.exports = class ProgressBar extends Plugin {
   install () {
   install () {
     const target = this.opts.target
     const target = this.opts.target
     const plugin = this
     const plugin = this
-    this.target = this.mount(target, plugin)
+    if (target) {
+      this.mount(target, plugin)
+    }
   }
   }
 
 
   uninstall () {
   uninstall () {

+ 3 - 1
src/plugins/StatusBar/index.js

@@ -132,7 +132,9 @@ module.exports = class StatusBarUI extends Plugin {
   install () {
   install () {
     const target = this.opts.target
     const target = this.opts.target
     const plugin = this
     const plugin = this
-    this.target = this.mount(target, plugin)
+    if (target) {
+      this.mount(target, plugin)
+    }
   }
   }
 
 
   uninstall () {
   uninstall () {

+ 5 - 37
src/uppy-react/Dashboard.js

@@ -1,7 +1,7 @@
 const React = require('react')
 const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const UppyCore = require('../core/Core')
 const UppyCore = require('../core/Core')
-const DashboardPlugin = require('../plugins/Dashboard')
+const UppyWrapper = require('./Wrapper')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,46 +10,14 @@ const h = React.createElement
  * renders the Dashboard inline, so you can put it anywhere you want.
  * renders the Dashboard inline, so you can put it anywhere you want.
  */
  */
 
 
-class Dashboard extends React.Component {
-  componentDidMount () {
-    const uppy = this.props.uppy
-    const options = Object.assign({}, this.props, {
-      target: this.container,
-      inline: true
-    })
-    delete options.uppy
-    uppy.use(DashboardPlugin, options)
-
-    this.plugin = uppy.getPlugin('DashboardUI')
-  }
-
-  componentWillUnmount () {
-    const uppy = this.props.uppy
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      ref: (container) => {
-        this.container = container
-      }
-    })
-  }
-}
+const Dashboard = (props) =>
+  h(UppyWrapper, props)
 
 
 Dashboard.propTypes = {
 Dashboard.propTypes = {
-  uppy: PropTypes.instanceOf(UppyCore).isRequired,
-  maxWidth: PropTypes.number,
-  maxHeight: PropTypes.number,
-  semiTransparent: PropTypes.bool,
-  defaultTabIcon: PropTypes.node,
-  showProgressDetails: PropTypes.bool,
-  locale: PropTypes.object
+  uppy: PropTypes.instanceOf(UppyCore).isRequired
 }
 }
-
 Dashboard.defaultProps = {
 Dashboard.defaultProps = {
-  locale: {}
+  plugin: 'DashboardUI'
 }
 }
 
 
 module.exports = Dashboard
 module.exports = Dashboard

+ 6 - 27
src/uppy-react/DashboardModal.js

@@ -1,7 +1,7 @@
 const React = require('react')
 const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const UppyCore = require('../core/Core')
 const UppyCore = require('../core/Core')
-const DashboardPlugin = require('../plugins/Dashboard')
+const UppyWrapper = require('./Wrapper')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -13,14 +13,8 @@ const h = React.createElement
 class DashboardModal extends React.Component {
 class DashboardModal extends React.Component {
   componentDidMount () {
   componentDidMount () {
     const uppy = this.props.uppy
     const uppy = this.props.uppy
-    const options = Object.assign({}, this.props, {
-      target: this.container,
-      onRequestHideModal: this.props.onRequestClose
-    })
-    delete options.uppy
-    uppy.use(DashboardPlugin, options)
 
 
-    this.plugin = uppy.getPlugin('DashboardUI')
+    this.plugin = uppy.getPlugin(this.props.plugin)
     if (this.props.open) {
     if (this.props.open) {
       this.plugin.showModal()
       this.plugin.showModal()
     }
     }
@@ -34,34 +28,19 @@ class DashboardModal extends React.Component {
     }
     }
   }
   }
 
 
-  componentWillUnmount () {
-    const uppy = this.props.uppy
-
-    uppy.removePlugin(this.plugin)
-  }
-
   render () {
   render () {
-    return h('div', {
-      ref: (container) => {
-        this.container = container
-      }
-    })
+    return h(UppyWrapper, this.props)
   }
   }
 }
 }
 
 
 DashboardModal.propTypes = {
 DashboardModal.propTypes = {
   uppy: PropTypes.instanceOf(UppyCore).isRequired,
   uppy: PropTypes.instanceOf(UppyCore).isRequired,
-  maxWidth: PropTypes.number,
-  maxHeight: PropTypes.number,
-  semiTransparent: PropTypes.bool,
-  defaultTabIcon: PropTypes.node,
-  showProgressDetails: PropTypes.bool,
-  onRequestClose: PropTypes.func,
-  locale: PropTypes.object
+  open: PropTypes.bool,
+  onRequestClose: PropTypes.func
 }
 }
 
 
 DashboardModal.defaultProps = {
 DashboardModal.defaultProps = {
-  locale: {}
+  plugin: 'DashboardUI'
 }
 }
 
 
 module.exports = DashboardModal
 module.exports = DashboardModal

+ 6 - 24
src/uppy-react/DragDrop.js

@@ -1,7 +1,7 @@
 const React = require('react')
 const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
-const UppyCore = require('../core/Core')
-const DragDropPlugin = require('../plugins/DragDrop')
+const UppyCore = require('../core')
+const UppyWrapper = require('./Wrapper')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,32 +10,14 @@ const h = React.createElement
  * uploaded.
  * uploaded.
  */
  */
 
 
-class DragDrop extends React.Component {
-  componentDidMount () {
-    const uppy = this.props.uppy
-    const options = Object.assign({}, this.props, {
-      target: this.container
-    })
-    delete options.uppy
-    uppy.use(DragDropPlugin, options)
-  }
-
-  render () {
-    return h('div', {
-      ref: (container) => {
-        this.container = container
-      }
-    })
-  }
-}
+const DragDrop = (props) =>
+  h(UppyWrapper, props)
 
 
 DragDrop.propTypes = {
 DragDrop.propTypes = {
-  uppy: PropTypes.instanceOf(UppyCore).isRequired,
-  locale: PropTypes.object
+  uppy: PropTypes.instanceOf(UppyCore).isRequired
 }
 }
-
 DragDrop.defaultProps = {
 DragDrop.defaultProps = {
-  locale: {}
+  plugin: 'DragDrop'
 }
 }
 
 
 module.exports = DragDrop
 module.exports = DragDrop

+ 5 - 23
src/uppy-react/ProgressBar.js

@@ -1,7 +1,7 @@
 const React = require('react')
 const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const UppyCore = require('../core/Core')
 const UppyCore = require('../core/Core')
-const ProgressBarPlugin = require('../plugins/ProgressBar')
+const UppyWrapper = require('./Wrapper')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,32 +10,14 @@ const h = React.createElement
  * uploaded.
  * uploaded.
  */
  */
 
 
-class ProgressBar extends React.Component {
-  componentDidMount () {
-    const uppy = this.props.uppy
-    const options = Object.assign({}, this.props, {
-      target: this.container
-    })
-    delete options.uppy
-    uppy.use(ProgressBarPlugin, options)
-  }
-
-  render () {
-    return h('div', {
-      ref: (container) => {
-        this.container = container
-      }
-    })
-  }
-}
+const ProgressBar = (props) =>
+  h(UppyWrapper, props)
 
 
 ProgressBar.propTypes = {
 ProgressBar.propTypes = {
-  uppy: PropTypes.instanceOf(UppyCore).isRequired,
-  locale: PropTypes.object
+  uppy: PropTypes.instanceOf(UppyCore).isRequired
 }
 }
-
 ProgressBar.defaultProps = {
 ProgressBar.defaultProps = {
-  locale: {}
+  plugin: 'ProgressBar'
 }
 }
 
 
 module.exports = ProgressBar
 module.exports = ProgressBar

+ 42 - 0
src/uppy-react/Wrapper.js

@@ -0,0 +1,42 @@
+const React = require('react')
+const PropTypes = require('prop-types')
+const UppyCore = require('../core')
+
+const h = React.createElement
+
+class UppyWrapper extends React.Component {
+  constructor (props) {
+    super(props)
+
+    this.refContainer = this.refContainer.bind(this)
+  }
+
+  componentDidMount () {
+    const plugin = this.props.uppy
+      .getPlugin(this.props.plugin)
+
+    plugin.mount(this.container, plugin)
+  }
+
+  componentWillUnmount () {
+    const plugin = this.props.uppy
+      .getPlugin(this.props.plugin)
+
+    plugin.unmount()
+  }
+
+  refContainer (container) {
+    this.container = container
+  }
+
+  render () {
+    return h('div', { ref: this.refContainer })
+  }
+}
+
+UppyWrapper.propTypes = {
+  uppy: PropTypes.instanceOf(UppyCore).isRequired,
+  plugin: PropTypes.string.isRequired
+}
+
+module.exports = UppyWrapper