diff --git a/src/diff/index.js b/src/diff/index.js index af4d02ea26..fc7b36b537 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -90,8 +90,11 @@ export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChi c.state = c._nextState; c._dirty = false; c._vnode = newVNode; - newVNode._dom = oldDom!=null ? oldVNode._dom : null; + newVNode._dom = oldDom!=null ? oldDom!==oldVNode._dom ? oldDom : oldVNode._dom : null; newVNode._children = oldVNode._children; + for (tmp = 0; tmp < newVNode._children.length; tmp++) { + if (newVNode._children[tmp]) newVNode._children[tmp]._parent = newVNode; + } break outer; } diff --git a/test/browser/lifecycles/shouldComponentUpdate.test.js b/test/browser/lifecycles/shouldComponentUpdate.test.js index bca70b8468..641a8afcfa 100644 --- a/test/browser/lifecycles/shouldComponentUpdate.test.js +++ b/test/browser/lifecycles/shouldComponentUpdate.test.js @@ -1,5 +1,5 @@ import { setupRerender } from 'preact/test-utils'; -import { createElement, render, Component } from '../../../src/index'; +import { createElement, render, Component, Fragment } from '../../../src/index'; import { setupScratch, teardown } from '../../_util/helpers'; /** @jsx createElement */ @@ -294,9 +294,9 @@ describe('Lifecycle methods', () => { }); // issue #1864 - it('should update dom pointers correctly', () => { + it('should update dom pointers correctly when returning an empty string', () => { function Child({ showMe, counter }) { - return showMe ?
Counter: {counter}
: null; + return showMe ?
Counter: {counter}
: ''; } class Parent extends Component { @@ -362,5 +362,192 @@ describe('Lifecycle methods', () => { rerender(); expect(scratch.textContent).to.equal(''); }); + + // issue #1864 second case + it('should update dom pointers correctly when returning a string', () => { + function Child({ showMe, counter }) { + return showMe ?
Counter: {counter}
: 'foo'; + } + + class Parent extends Component { + shouldComponentUpdate() { + return false; + } + render() { + return ; + } + } + + let updateChild = () => null; + class Inner extends Component { + constructor(props) { + super(props); + this.state = { showMe: false }; + updateChild = () => { + this.setState({ showMe: display = !display }); + }; + } + render() { + return ; + } + } + + let display = false; + let updateApp = () => null; + class App extends Component { + constructor(props) { + super(props); + updateApp = () => this.setState({}); + } + render() { + return ( +
+
+
+ +
+ ); + } + } + + render(, scratch); + expect(scratch.textContent).to.equal('foo'); + + updateChild(); + rerender(); + + expect(scratch.textContent).to.equal('Counter: 0'); + + updateApp(); + rerender(); + + expect(scratch.textContent).to.equal('Counter: 0'); + + updateChild(); + rerender(); + + expect(scratch.textContent).to.equal('foo'); + + updateApp(); + rerender(); + expect(scratch.textContent).to.equal('foo'); + }); + + it('should correctly update nested chilreen', () => { + let hideThree, incrementThree; + + class One extends Component { + shouldComponentUpdate() { return false; } + render(p) { return p.children; } + } + + class Two extends Component { + constructor(props) { + super(props); + this.state = { hideMe: false }; + hideThree = () => this.setState(s => ({ hideMe: !s.hideMe })); + } + + shouldComponentUpdate(nextProps, nextState) { return this.state.hideMe !== nextState.hideMe; } + + render(p, { hideMe }) { + return hideMe ? : p.children; + } + } + + class Three extends Component { + constructor(props) { + super(props); + this.state = { counter: 1 }; + incrementThree = () => this.setState(s => ({ counter: s.counter + 1 })); + } + + render(p, { counter }) { return {counter}; } + } + + render(, scratch); + expect(scratch.innerHTML).to.equal('1'); + + hideThree(); + rerender(); + expect(scratch.innerHTML).to.equal(''); + + hideThree(); + rerender(); + expect(scratch.innerHTML).to.equal('1'); + + incrementThree(); + rerender(); + expect(scratch.innerHTML).to.equal('2'); + }); + + // issue #1864 third case + it('should update dom pointers correctly without siblings', () => { + function Child({ showMe, counter }) { + return showMe ?
Counter: {counter}
: 'foo'; + } + + class Parent extends Component { + shouldComponentUpdate() { + return false; + } + render() { + return ; + } + } + + let updateChild = () => null; + class Inner extends Component { + constructor(props) { + super(props); + this.state = { showMe: false }; + updateChild = () => { + this.setState({ showMe: display = !display }); + }; + } + render() { + return ; + } + } + + let display = false; + let updateApp = () => null; + class App extends Component { + constructor(props) { + super(props); + updateApp = () => this.setState({}); + } + render() { + return ( +
+ +
+ ); + } + } + + render(, scratch); + expect(scratch.textContent).to.equal('foo'); + + updateChild(); + rerender(); + + expect(scratch.textContent).to.equal('Counter: 0'); + + updateApp(); + rerender(); + + expect(scratch.textContent).to.equal('Counter: 0'); + + updateChild(); + rerender(); + + expect(scratch.textContent).to.equal('foo'); + + updateApp(); + rerender(); + + expect(scratch.textContent).to.equal('foo'); + }); }); });