lwc
lwc copied to clipboard
`for:each` within `for:each` without explicit `key` can lead to vdom errors
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
This issue has been linked to a new work item: W-17266520
Potential fix: we could use a VFragment for for:each. This may help the vdom diffing to distinguish between different for:each blocks.
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