Skip to content

mismatched child nodes count in patchBlockChildren when rendering component returning render function in JSX from computed() with <slot v-if="ref"> #10319

Open
@n0099

Description

@n0099

Vue version

3.4.18

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-cs8k7r?file=src%2FScopedSlots.vue%2Csrc%2FApp.vue

Steps to reproduce

Click the Toggle flag button.

What is expected?

No exception was thrown.

What is actually happening?

TypeError: Cannot read properties of undefined (reading 'el')
    at patchBlockChildren (chunk-EVMNRHZZ.js?v=f93e5a51:7163:18)
    at processFragment (chunk-EVMNRHZZ.js?v=f93e5a51:7266:9)
    at patch (chunk-EVMNRHZZ.js?v=f93e5a51:6758:9)
    at ReactiveEffect.componentUpdateFn [as fn] (chunk-EVMNRHZZ.js?v=f93e5a51:7542:9)
    at ReactiveEffect.run (chunk-EVMNRHZZ.js?v=f93e5a51:227:19)
    at instance.update (chunk-EVMNRHZZ.js?v=f93e5a51:7586:17)
    at callWithErrorHandling (chunk-EVMNRHZZ.js?v=f93e5a51:1667:32)
    at flushJobs (chunk-EVMNRHZZ.js?v=f93e5a51:1874:9)

System Info

No response

Any additional comments?

By adding a breakpoint just before the exception was thrown:

, we can see the length of two children params is mismatched, oldChildren.length will always be less one than the newChildren.length:
image

The <FontAwesomeIcon> is returning a render function h() within computed() to dynamically build elements by the component props:
https://github.com/FortAwesome/vue-fontawesome/blob/560b07aade4b8b1f909b448391968935ff9a759c/src/components/FontAwesomeIcon.js#L175
https://github.com/FortAwesome/vue-fontawesome/blob/560b07aade4b8b1f909b448391968935ff9a759c/index.js#L296
https://github.com/FortAwesome/vue-fontawesome/blob/560b07aade4b8b1f909b448391968935ff9a759c/index.js#L329-L337

The following approaches will prevent this exception:

  • Remove v-if="!flag" from <slot>
- <slot v-if="!flag" :renderer="renderer" />
+ <slot :renderer="renderer" />
- <slot v-if="!flag" :renderer="renderer" />
+ <slot v-if="!flag">
+   <RenderFunction :renderer="renderer" />
+ </slot>
- <ScopedSlots>
-   <template #default="{ renderer }">
-     <span>
-       <RenderFunction :renderer="renderer" />
-     </span>
-   </template>
- </ScopedSlots>
+ <ScopedSlots />
  const flag = ref(true);
- const renderer = computed(() => (
+ const renderer = (
    <>{flag.value === false && <FontAwesomeIcon icon="times" />}</>
- ));
+ );

leads to clicking the button will not re-create VNode from renderer, but if the initial value of flag is true its reactivity will somehow be resumed:

- const flag = ref(true);
+ const flag = ref(false);
  • Replace the component <FontAwesomeIcon> that is based on a render function with any other component using plain <template> or just some HTMLElement
- <>{flag.value === false && <FontAwesomeIcon icon="times" />}</>
+ <>{flag.value === false && <span>{JSON.stringify(flag.value)}</span>}</>
+ <>{flag.value === false && <div></div>}</>

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already exists

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions