From f5738915a0d67c87f54f0ccd5b946e7a4ce0d5c1 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Fri, 19 Jul 2024 11:12:26 +0200 Subject: [PATCH] fix(child-diffing): Should shift keyed fragmented lists (#4448) * Should shift keyed fragmented lists * cleanup old usage of oldDom --- src/diff/children.js | 16 +++++------ test/browser/render.test.js | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/diff/children.js b/src/diff/children.js index 1e44c5cade..93064c37dd 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -116,14 +116,6 @@ export function diffChildren( childVNode._flags & INSERT_VNODE || oldVNode._children === childVNode._children ) { - if ( - oldDom && - typeof childVNode.type == 'string' && - // @ts-expect-error olDom should be present on a DOM node - !parentDom.contains(oldDom) - ) { - oldDom = getDomSibling(oldVNode); - } oldDom = insert(childVNode, oldDom, parentDom); } else if ( typeof childVNode.type == 'function' && @@ -378,6 +370,14 @@ function insert(parentVNode, oldDom, parentDom) { return oldDom; } else if (parentVNode._dom != oldDom) { + if ( + oldDom && + parentVNode.type && + // @ts-expect-error olDom should be present on a DOM node + !parentDom.contains(oldDom) + ) { + oldDom = getDomSibling(parentVNode); + } parentDom.insertBefore(parentVNode._dom, oldDom || null); oldDom = parentVNode._dom; } diff --git a/test/browser/render.test.js b/test/browser/render.test.js index 4755607c12..bb2c2d4adb 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -1689,4 +1689,58 @@ describe('render()', () => { ]); clearLog(); }); + + it('should shift keyed lists with wrapping fragment-like children', () => { + const ItemA = ({ text }) =>
A: {text}
; + const ItemB = ({ text }) =>
B: {text}
; + + const Item = ({ text, type }) => { + return type === 'B' ? : ; + }; + + let set; + class App extends Component { + constructor(props) { + super(props); + this.state = { items: a, mapping: mappingA }; + set = (items, mapping) => { + this.setState({ items, mapping }); + }; + } + + render() { + return ( +
    + {this.state.items.map((key, i) => ( + + ))} +
+ ); + } + } + + const a = ['4', '1', '2', '3']; + const mappingA = ['A', 'A', 'B', 'B']; + const b = ['1', '2', '4', '3']; + const mappingB = ['B', 'A', 'A', 'A']; + const c = ['4', '2', '1', '3']; + const mappingC = ['A', 'B', 'B', 'A']; + + render(, scratch); + expect(scratch.innerHTML).to.equal( + '
    A: 4
    A: 1
    B: 2
    B: 3
' + ); + + set(b, mappingB); + rerender(); + expect(scratch.innerHTML).to.equal( + '
    B: 1
    A: 2
    A: 4
    A: 3
' + ); + + set(c, mappingC); + rerender(); + expect(scratch.innerHTML).to.equal( + '
    A: 4
    B: 2
    B: 1
    A: 3
' + ); + }); });