From 4c8da0e5aa448d5d0e6cad0cd11aaca063b6ff90 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Sun, 10 Nov 2024 18:33:40 +0100 Subject: [PATCH] Recreate unkeyed functional components when they change position. With keyed children we can confidently track them as they move throughout an array of components. When this happens with unkeyed functional components we should not risk reusing the state as that could possibly make us re-use state in unrelated components, as seen in #2949. --- src/diff/children.js | 3 ++- test/browser/render.test.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/diff/children.js b/src/diff/children.js index ba2730478c..84d550e8ae 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -398,8 +398,9 @@ function findMatchingIndex( // if the oldVNode was null or matched, then there could needs to be at least // 1 (aka `remainingOldChildren > 0`) children to find and compare against. let shouldSearch = + (typeof type !== 'function' || type === Fragment || key) && remainingOldChildren > - (oldVNode != null && (oldVNode._flags & MATCHED) === 0 ? 1 : 0); + (oldVNode != null && (oldVNode._flags & MATCHED) === 0 ? 1 : 0); if ( oldVNode === null || diff --git a/test/browser/render.test.js b/test/browser/render.test.js index 96bef10bb0..39b0ad86ba 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -1786,6 +1786,37 @@ describe('render()', () => { ); }); + // #2949 + it('should not swap unkeyed chlildren', () => { + class X extends Component { + constructor(props) { + super(props); + this.name = props.name; + } + render() { + return

{this.name}

; + } + } + + function Foo({ condition }) { + return ( +
+ {condition ? '' : } + {condition ? : ''} +
+ ); + } + + render(, scratch); + expect(scratch.textContent).to.equal('A'); + + render(, scratch); + expect(scratch.textContent).to.equal('B'); + + render(, scratch); + expect(scratch.textContent).to.equal('A'); + }); + it('handle shuffled (stress test)', () => { function randomize(arr) { for (let i = arr.length - 1; i > 0; i--) {