diff --git a/src/component.js b/src/component.js index 2270391120..88817c2d10 100644 --- a/src/component.js +++ b/src/component.js @@ -103,7 +103,7 @@ export function getDomSibling(vnode, childIndex) { // Since updateParentDomPointers keeps _dom pointer correct, // we can rely on _dom to tell us if this subtree contains a // rendered DOM node, and what the first rendered DOM node is - return sibling._dom; + return sibling._nextDom || sibling._dom; } } diff --git a/src/diff/children.js b/src/diff/children.js index fdd6a1255c..35874a64e1 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -111,6 +111,7 @@ export function diffChildren( oldVNode = oldChildren[i]; if (oldVNode && oldVNode.key == null && oldVNode._dom) { if (oldVNode._dom == oldDom) { + oldVNode._parent = oldParentVNode; oldDom = getDomSibling(oldVNode); } diff --git a/test/browser/fragments.test.js b/test/browser/fragments.test.js index fe3a672016..f04a80d3a7 100644 --- a/test/browser/fragments.test.js +++ b/test/browser/fragments.test.js @@ -646,6 +646,110 @@ describe('Fragment', () => { expect(scratch.innerHTML).to.equal('
Hello
'); }); + it('should preserve order for fragment switching', () => { + let set; + class Foo extends Component { + constructor(props) { + super(props); + this.state = { isLoading: true, data: null }; + set = this.setState.bind(this); + } + render(props, { isLoading, data }) { + return ( + +
HEADER
+ {isLoading ?
Loading...
: null} + {data ?
Content: {data}
: null} +
+ ); + } + } + + render(, scratch); + expect(scratch.innerHTML).to.equal( + '
HEADER
Loading...
' + ); + + set({ isLoading: false, data: 2 }); + rerender(); + expect(scratch.innerHTML).to.equal( + '
HEADER
Content: 2
' + ); + }); + + it('should preserve order for nested fragment switching w/ child return', () => { + let set; + const Wrapper = ({ children }) => {children}; + class Foo extends Component { + constructor(props) { + super(props); + this.state = { isLoading: true, data: null }; + set = this.setState.bind(this); + } + render(props, { isLoading, data }) { + return ( + +
HEADER
+ {isLoading ?
Loading...
: null} + {data ?
Content: {data}
: null} +
+ ); + } + } + + render( + + + , + scratch + ); + expect(scratch.innerHTML).to.equal( + '
HEADER
Loading...
' + ); + + set({ isLoading: false, data: 2 }); + rerender(); + expect(scratch.innerHTML).to.equal( + '
HEADER
Content: 2
' + ); + }); + + it('should preserve order for nested fragment switching', () => { + let set; + const Wrapper = () => ( + + + + ); + class Foo extends Component { + constructor(props) { + super(props); + this.state = { isLoading: true, data: null }; + set = this.setState.bind(this); + } + render(props, { isLoading, data }) { + return ( + +
HEADER
+ {isLoading ?
Loading...
: null} + {data ?
Content: {data}
: null} +
+ ); + } + } + + render(, scratch); + expect(scratch.innerHTML).to.equal( + '
HEADER
Loading...
' + ); + + set({ isLoading: false, data: 2 }); + rerender(); + expect(scratch.innerHTML).to.equal( + '
HEADER
Content: 2
' + ); + }); + it('should preserve state with reordering in multiple levels', () => { function Foo({ condition }) { return condition ? (