@@ -23,7 +23,8 @@ import { getDomSibling } from '../component';
2323 * Diff the children of a virtual node
2424 * @param {PreactElement } parentDom The DOM element whose children are being
2525 * diffed
26- * @param {ComponentChildren[] } renderResult
26+ * @param {ComponentChildren|ComponentChildren[] } renderResult A single child or
27+ * an array of them; single children are not wrapped by the caller.
2728 * @param {VNode } newParentVNode The new virtual node whose children should be
2829 * diff'ed against oldParentVNode
2930 * @param {VNode } oldParentVNode The old virtual node whose children should be
@@ -71,11 +72,16 @@ export function diffChildren(
7172 /** @type {VNode[] } */
7273 let oldChildren = oldParentVNode . _children || EMPTY_ARR ;
7374
74- let newChildrenLength = renderResult . length ;
75+ // `renderResult` is passed unwrapped: the overwhelming majority of children
76+ // diffs are a single child (~84% in the wild, see #2618), so wrapping it in
77+ // a throwaway `[child]` array at the call site is pure allocation churn.
78+ let isArr = isArray ( renderResult ) ;
79+ let newChildrenLength = isArr ? renderResult . length : 1 ;
7580
7681 oldDom = constructNewChildrenArray (
7782 newParentVNode ,
7883 renderResult ,
84+ isArr ,
7985 oldChildren ,
8086 oldDom ,
8187 newChildrenLength
@@ -158,12 +164,14 @@ export function diffChildren(
158164
159165/**
160166 * @param {VNode } newParentVNode
161- * @param {ComponentChildren[] } renderResult
167+ * @param {ComponentChildren|ComponentChildren[] } renderResult
168+ * @param {boolean } isArr Whether `renderResult` is an array of children
162169 * @param {VNode[] } oldChildren
163170 */
164171function constructNewChildrenArray (
165172 newParentVNode ,
166173 renderResult ,
174+ isArr ,
167175 oldChildren ,
168176 oldDom ,
169177 newChildrenLength
@@ -184,18 +192,19 @@ function constructNewChildrenArray(
184192 * real reorder happened and we need to compute the minimal set of moves. */
185193 let moved = false ;
186194
187- newParentVNode . _children = new Array ( newChildrenLength ) ;
195+ /** @type {VNode[] } */
196+ let newChildren = ( newParentVNode . _children = new Array ( newChildrenLength ) ) ;
188197 for ( i = 0 ; i < newChildrenLength ; i ++ ) {
189198 // @ts -expect-error We are reusing the childVNode variable to hold both the
190199 // pre and post normalized childVNode
191- childVNode = renderResult [ i ] ;
200+ childVNode = isArr ? renderResult [ i ] : renderResult ;
192201
193202 if (
194203 childVNode == NULL ||
195204 typeof childVNode == 'boolean' ||
196205 typeof childVNode == 'function'
197206 ) {
198- newParentVNode . _children [ i ] = NULL ;
207+ newChildren [ i ] = NULL ;
199208 continue ;
200209 }
201210 // If this newVNode is being reused (e.g. <div>{reuse}{reuse}</div>) in the same diff,
@@ -208,15 +217,15 @@ function constructNewChildrenArray(
208217 typeof childVNode != 'object' ||
209218 childVNode . constructor == String
210219 ) {
211- childVNode = newParentVNode . _children [ i ] = createVNode (
220+ childVNode = newChildren [ i ] = createVNode (
212221 NULL ,
213222 childVNode ,
214223 NULL ,
215224 NULL ,
216225 NULL
217226 ) ;
218227 } else if ( isArray ( childVNode ) ) {
219- childVNode = newParentVNode . _children [ i ] = createVNode (
228+ childVNode = newChildren [ i ] = createVNode (
220229 Fragment ,
221230 { children : childVNode } ,
222231 NULL ,
@@ -228,15 +237,15 @@ function constructNewChildrenArray(
228237 // scenario:
229238 // const reuse = <div />
230239 // <div>{reuse}<span />{reuse}</div>
231- childVNode = newParentVNode . _children [ i ] = createVNode (
240+ childVNode = newChildren [ i ] = createVNode (
232241 childVNode . type ,
233242 childVNode . props ,
234243 childVNode . key ,
235244 childVNode . ref ? childVNode . ref : NULL ,
236245 childVNode . _original
237246 ) ;
238247 } else {
239- newParentVNode . _children [ i ] = childVNode ;
248+ newChildren [ i ] = childVNode ;
240249 }
241250
242251 const skewedIndex = i + skew ;
@@ -331,7 +340,7 @@ function constructNewChildrenArray(
331340 /** @type {number[] } length of the longest increasing subsequence ending at child i */
332341 let lisLengths = [ ] ;
333342 for ( i = 0 ; i < newChildrenLength ; i ++ ) {
334- childVNode = newParentVNode . _children [ i ] ;
343+ childVNode = newChildren [ i ] ;
335344 if ( childVNode && childVNode . _flags & MATCHED ) {
336345 // Binary search for the insertion point, keeping the pass at
337346 // O(n log n) even for pathological reorders.
@@ -359,7 +368,7 @@ function constructNewChildrenArray(
359368 if ( lisLengths [ i ] == skew ) {
360369 skew -- ;
361370 } else {
362- newParentVNode . _children [ i ] . _flags |= INSERT_VNODE ;
371+ newChildren [ i ] . _flags |= INSERT_VNODE ;
363372 }
364373 }
365374 }
0 commit comments