diff --git a/src/diff/children.js b/src/diff/children.js index 8256e45d74..71879d3166 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -62,7 +62,6 @@ export function diffChildren( for (i = 0; i < newChildrenLength; i++) { childVNode = newParentVNode._children[i]; - if ( childVNode == null || typeof childVNode == 'boolean' || @@ -231,11 +230,15 @@ function constructNewChildrenArray(newParentVNode, renderResult, oldChildren) { // Handle unmounting null placeholders, i.e. VNode => null in unkeyed children if (childVNode == null) { oldVNode = oldChildren[i]; - if (oldVNode && oldVNode.key == null && oldVNode._dom) { + if ( + oldVNode && + oldVNode.key == null && + oldVNode._dom && + (oldVNode._flags & MATCHED) === 0 + ) { if (oldVNode._dom == newParentVNode._nextDom) { newParentVNode._nextDom = getDomSibling(oldVNode); } - unmount(oldVNode, oldVNode, false); // Explicitly nullify this position in oldChildren instead of just @@ -250,7 +253,6 @@ function constructNewChildrenArray(newParentVNode, renderResult, oldChildren) { oldChildren[i] = null; remainingOldChildren--; } - continue; } diff --git a/test/browser/render.test.js b/test/browser/render.test.js index 2baf381d3a..3be0393a20 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -1308,4 +1308,52 @@ describe('render()', () => { expect(divs[1].hasAttribute('role')).to.equal(false); expect(divs[2].hasAttribute('role')).to.equal(false); }); + + it('should not crash or repeatedly add the same child when replacing a matched vnode with null', () => { + const B = () =>
B
; + + let update; + class App extends Component { + constructor(props) { + super(props); + this.state = { show: true }; + update = () => { + this.setState(state => ({ show: !state.show })); + }; + } + + render() { + if (this.state.show) { + return ( +
+ +
+
+ ); + } + return ( +
+
+ {null} + +
+ ); + } + } + + render(, scratch); + expect(scratch.innerHTML).to.equal('
B
'); + + update(); + rerender(); + expect(scratch.innerHTML).to.equal('
B
'); + + update(); + rerender(); + expect(scratch.innerHTML).to.equal('
B
'); + + update(); + rerender(); + expect(scratch.innerHTML).to.equal('
B
'); + }); });