From d1d884b21e91fbfda84b85bc11bc0a769b9bc1c8 Mon Sep 17 00:00:00 2001 From: Bill Neff Date: Sun, 28 Jan 2018 17:50:44 -0500 Subject: [PATCH 1/3] force-update-fix Fix bug where portal is added to dom twice if parent does forceUpdate during intial render --- src/preact-portal.js | 4 ++-- test/preact-portal.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/preact-portal.js b/src/preact-portal.js index 1970559..b57683f 100644 --- a/src/preact-portal.js +++ b/src/preact-portal.js @@ -10,7 +10,7 @@ export default class Portal extends Component { componentDidUpdate(props) { for (let i in props) { if (props[i]!==this.props[i]) { - return this.renderLayer(); + return setTimeout(this.renderLayer); } } } @@ -28,7 +28,7 @@ export default class Portal extends Component { return typeof node==='string' ? document.querySelector(node) : node; } - renderLayer(show=true) { + renderLayer = (show=true) => { // clean up old node if moving bases: if (this.props.into!==this.intoPointer) { this.intoPointer = this.props.into; diff --git a/test/preact-portal.js b/test/preact-portal.js index 0cdcc71..0db3672 100644 --- a/test/preact-portal.js +++ b/test/preact-portal.js @@ -69,4 +69,32 @@ describe('preact-portal', () => { .to.have.been.calledOnce .and.calledWith({}, ctx); }); + + it('should only create one instance when forced to rerender by parent during first render', () => { + + let foo = document.createElement('div'); + foo.setAttribute('id', 'foo'); + scratch.appendChild(foo); + + let base = document.createElement('div'); + scratch.appendChild(base); + + class ParentForceUpdateOnMount extends Component { + + componentDidMount() { + this.forceUpdate(); + } + + render() { + return ( +
hello
+ ); + } + } + + render(, base); + + expect(foo).to.have.property('innerHTML', '
hello
'); + + }); }); From afdc7469db99dc51ad0ebd47b26ffed4be2c26a6 Mon Sep 17 00:00:00 2001 From: Bill Neff Date: Sun, 28 Jan 2018 20:54:32 -0500 Subject: [PATCH 2/3] force-update-fix use bind instead of arrow function to avoid making code larger in transpilation --- src/preact-portal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/preact-portal.js b/src/preact-portal.js index b57683f..53aee32 100644 --- a/src/preact-portal.js +++ b/src/preact-portal.js @@ -16,6 +16,7 @@ export default class Portal extends Component { } componentDidMount() { + this.renderLayer = this.renderLayer.bind(this); this.renderLayer(); } @@ -28,7 +29,7 @@ export default class Portal extends Component { return typeof node==='string' ? document.querySelector(node) : node; } - renderLayer = (show=true) => { + renderLayer(show=true) { // clean up old node if moving bases: if (this.props.into!==this.intoPointer) { this.intoPointer = this.props.into; From 181774387b1a767631a3d0d92b823cabdede8bc9 Mon Sep 17 00:00:00 2001 From: Bill Neff Date: Mon, 29 Jan 2018 10:00:03 -0500 Subject: [PATCH 3/3] force-update-fix fix bug where delayed render from componentDidMount can be called after unmount, which re-adds the unmounted portal upgrade preact to latest and fix failing test case from that --- package.json | 8 ++++---- src/preact-portal.js | 4 ++++ test/preact-portal.js | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 513efef..87bad26 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "babel-preset-es2015-minimal-rollup": "^2.0.0", "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", - "chai": "^3.5.0", + "chai": "^4.1.2", "eslint": "^3.4.0", "gzip-size-cli": "^1.0.0", "karma": "^1.2.0", @@ -63,16 +63,16 @@ "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^1.7.0", "mkdirp": "^0.5.1", - "mocha": "^3.0.2", + "mocha": "^5.0.0", "npm-run-all": "^3.0.0", "phantomjs-prebuilt": "^2.1.4", - "preact": "^5.7.0", + "preact": "^8.2.7", "pretty-bytes-cli": "^1.0.0", "rollup": "^0.34.11", "rollup-plugin-babel": "^2.4.0", "rollup-plugin-commonjs": "^3.3.1", "rollup-plugin-node-resolve": "^2.0.0", - "sinon": "^1.17.2", + "sinon": "^4.2.2", "sinon-chai": "^2.8.0", "uglify-js": "^2.6.2", "webpack": "^1.12.14" diff --git a/src/preact-portal.js b/src/preact-portal.js index 53aee32..642ab0d 100644 --- a/src/preact-portal.js +++ b/src/preact-portal.js @@ -16,12 +16,14 @@ export default class Portal extends Component { } componentDidMount() { + this.isMounted=true; this.renderLayer = this.renderLayer.bind(this); this.renderLayer(); } componentWillUnmount() { this.renderLayer(false); + this.isMounted=false; if (this.remote) this.remote.parentNode.removeChild(this.remote); } @@ -30,6 +32,8 @@ export default class Portal extends Component { } renderLayer(show=true) { + if (!this.isMounted) return; + // clean up old node if moving bases: if (this.props.into!==this.intoPointer) { this.intoPointer = this.props.into; diff --git a/test/preact-portal.js b/test/preact-portal.js index 0db3672..2347f83 100644 --- a/test/preact-portal.js +++ b/test/preact-portal.js @@ -67,7 +67,7 @@ describe('preact-portal', () => { expect(Child) .to.have.been.calledOnce - .and.calledWith({}, ctx); + .and.calledWith({children: []}, ctx); }); it('should only create one instance when forced to rerender by parent during first render', () => {