Defer component initialization until element is requested

This fixes the error snapshotting Docks described in #16864.
This commit is contained in:
Matthew Dapena-Tretter
2018-04-02 17:10:37 -07:00
parent 9a81957368
commit dfd3ba8c14

View File

@@ -50,7 +50,6 @@ module.exports = class Dock {
})
this.state = {
ready: false,
size: null,
visible: false,
shouldAnimate: false
@@ -70,8 +69,6 @@ module.exports = class Dock {
this.paneContainer.onDidChangeActivePaneItem((item) => params.didChangeActivePaneItem(this, item)),
this.paneContainer.onDidDestroyPaneItem((item) => params.didDestroyPaneItem(item))
)
etch.initialize(this)
}
// This method is called explicitly by the object which adds the Dock to the document.
@@ -81,11 +78,11 @@ module.exports = class Dock {
}
getElement () {
if (!this.state.ready) {
// Render the element with its contents for the first time. This needs to be deferred so it's
// not done when snapshotting.
this.setState({ready: true})
etch.updateSync(this)
// Because this code is included in the snapshot, we have to make sure we don't touch the DOM
// during initialization. Therefore, we defer initialization of the component (which creates a
// DOM element) until somebody asks for the element.
if (this.element == null) {
etch.initialize(this)
}
return this.element
}
@@ -166,8 +163,10 @@ module.exports = class Dock {
// Render immediately if the dock becomes visible or the size changes in case people are
// measuring after opening, for example.
if ((visible && !prevState.visible) || (this.state.size !== prevState.size)) etch.updateSync(this)
else etch.update(this)
if (this.element != null) {
if ((visible && !prevState.visible) || (this.state.size !== prevState.size)) etch.updateSync(this)
else etch.update(this)
}
if (hovered !== prevState.hovered) {
this.emitter.emit('did-change-hovered', hovered)
@@ -178,14 +177,6 @@ module.exports = class Dock {
}
render () {
const atomDock = children => $('atom-dock', {className: this.location}, children)
// Because this code is included in the snapshot, we have to make sure we don't load
// DOM-touching classes (like PaneContainerElement) during initialization. The way we do this
// is by avoiding rendering the full contents until the element is attached, at which point we
// toggle the `ready` state and render the full dock contents.
if (!this.state.ready) return atomDock([])
const innerElementClassList = ['atom-dock-inner', this.location]
if (this.state.visible) innerElementClassList.push(VISIBLE_CLASS)
@@ -207,7 +198,9 @@ module.exports = class Dock {
// ...but the content needs to maintain a constant size.
const wrapperStyle = {[this.widthOrHeight]: `${size}px`}
return atomDock(
return $(
'atom-dock',
{className: this.location},
$.div(
{ref: 'innerElement', className: innerElementClassList.join(' ')},
$.div(