lwc icon indicating copy to clipboard operation
lwc copied to clipboard

`for:each` within `for:each` without explicit `key` can lead to vdom errors

Open nolanlawson opened this issue 1 year ago • 3 comments

(Salesforce Known Issue)

If you have an iteration (for:each) within another iteration:

<template>
    <template for:each={children} for:item="child">
        <x-row lwc:if={child.renderMe} prop={child} key={child.id}></x-row>
        <template lwc:if={child.children} for:each={child.children} for:item="grandchild">
            <x-row lwc:if={grandchild.renderMe} prop={grandchild} key={grandchild.id}></x-row>
        </template>
    </template>
</template>

... and if you have a sufficient number of lwc:if layers and duplicated keys between the grandchildren and the children, then you can get a runtime error:

TypeError: Cannot use 'in' operator to search for 'prop' in undefined

Thrown by:

https://github.com/salesforce/lwc/blob/666e8fe759786c08a5f23eff117843b851a0667b/packages/%40lwc/engine-core/src/framework/modules/props.ts#L62

Or:

TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.

Thrown by:

https://github.com/salesforce/lwc/blob/666e8fe759786c08a5f23eff117843b851a0667b/packages/%40lwc/engine-dom/src/apis/create-element.ts#L76

One workaround is to put a key around the inner loop:

 <template>
     <template for:each={children} for:item="child">
         <x-row lwc:if={child.renderMe} prop={child} key={child.id}></x-row>
+        <div key={child.id}>
         <template lwc:if={child.children} for:each={child.children} for:item="grandchild">
             <x-row lwc:if={grandchild.renderMe} prop={grandchild} key={grandchild.id}></x-row>
         </template>
+        </div>
     </template>
 </template>

Another workaround is to flatten the array. E.g.:

get flattenedArray() {
  return this.children.map(child => [child, ...child.children]).flat() // something like this
}

... and then use a single for:each:

<template for:each={flattenedArray} for:item="child">
    <!-- ... --->
</template>

Repro for first error: https://github.com/nolanlawson/lwc-barebone/commit/836362a7dfd7367d8e669a6771b1125cf039aa8b Repro for second error: https://github.com/nolanlawson/lwc-barebone/commit/a97223ca06be2e683ecfd767df4f7641024afcfd

Repro steps: pnpm i && pnpm run dev

nolanlawson avatar Nov 18 '24 23:11 nolanlawson

This issue has been linked to a new work item: W-17266520

git2gus[bot] avatar Nov 18 '24 23:11 git2gus[bot]

Potential fix: we could use a VFragment for for:each. This may help the vdom diffing to distinguish between different for:each blocks.

nolanlawson avatar Nov 19 '24 23:11 nolanlawson

Interestingly, my attempt to use a VFragment did not resolve the issue, and caused 4 other test failures in the Karma tests.

The issue here seems to be that we land in this code path but the elm on both the n1 and n2 are undefined:

https://github.com/salesforce/lwc/blob/5d1837bdcdf75724e806a34b55c50acefd753f5d/packages/%40lwc/engine-core/src/framework/rendering.ts#L393-L395

nolanlawson avatar Dec 17 '24 19:12 nolanlawson