Skip to content

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

Open
@nolanlawson

Description

@nolanlawson

(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:

Or:

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

Thrown by:

const removedNode = removeChild.call(this, oldChild);

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: nolanlawson/lwc-barebone@836362a
Repro for second error: nolanlawson/lwc-barebone@a97223c

Repro steps: pnpm i && pnpm run dev

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions