Subscribe on changes!

Template is not updating when passing vnode to Dynamic Component

avatar
Nov 4th 2021

Version

3.2.21

Reproduction link

https://sfc.vuejs.org/

Steps to reproduce

  • click on some checkbox

What is expected?

Template should update to show information about the selected fields

What is actually happening?

Nothing happens even though "render" function is called.

Additional information

There's a workaround for this issue. Instead of using <component :is="vnode"> I replaced it with a Functional component that receives a vnode as a prop and returns it:

const Children = ({ children }) => children

but I am not sure if this is the correct way to handle my use case or if it is a bug that needs to be fixed.

avatar
Nov 4th 2021

Just wanted to add that I have a use case for rendering an existing vnode in the template. I'm building a DataTable component where my api, for example returns a list of table headers. Each object contains a "render" function that returns already created vnode. Unfortunately, Dynamic component is the only "official" way to render existing vnode inside of SFC and without workaround posted above, it doesn't work as it should - example

avatar
Nov 5th 2021

Seems like a bug, definitely. The new subtree is present in the instance's vnode, but DOM wasn't patched:

image

avatar
Nov 5th 2021

return a render function will work. and i did not think it is a bug.

return () => h('div', {
      style: { color: isChecked ? 'green' : 'red' },
    }, `[ ${id} ] ${isChecked ? 'checked' : 'unchecked'}`)
avatar
Nov 5th 2021

Actually, it's a bug. should not take fast path when dynamicChildren is empty. image

the root cause is we doesn't track block here. image

avatar
Nov 5th 2021

@edison1105 while this issue is still open. Do you know why I also can't render a Fragment which may have falsy values as its children? Will your PR fix this case? check example. It works in plain JS though

avatar
Nov 6th 2021

@sqal this code not working because component should be a vnode not an vnode array.

  const array = () => [
    h('div', 'foo'),
    h('div', 'bar')
  ]

should change to

  const array = () =>  h(Fragment,null,[
    h('div', 'foo'),
    h('div', 'bar')
  ])

this code not working because every item in children array must be a vnode.

    return h(Fragment, null, [
      h('div', 'foo'),
      // ERROR: Cannot read properties of null (reading 'el')
        null,
      //false,
      h('div', 'bar'),
    ])
avatar
Nov 6th 2021

@edison1105

every item in children array must be a vnode

hmm but doesn't have to when using render function in plain JS? (check Comp.vue in my demo),. A little bit of inconsistency here, but if I have to filter out falsy values myself that's fine.

avatar
Nov 6th 2021

@sqal A vnode is the result of template compilation. If you want to create a vnode with the h function, you have to make sure it is correct and can be rendered into the DOM.