Search code examples
vue.jsvuejs3vue-component

Register for unmount of component


I implemented a composable which can mount a component just inside a typescript function without defining it in the <template> section.

export const useMountDialog = () => {
  const appContext = inject<AppContext>('$useMountDialog')
  if (!appContext) {
    throw new Error(
      'Could not find app context. Make sure to provide the app instance.'
    )
  }

  const mountDialog = <T extends { new (...args: any[]): any }>(
    component: T,
    props: ExtractPropTypes<InstanceType<T>>
  ) => {
    const container = document.createElement('div')
    const vNode = h(component, props)
    vNode.appContext = appContext
    render(vNode, container)

    const unmount = () => {
      console.log('Unmount')
      // document.body.removeChild(container);
      render(null, container)
    }

    document.body.appendChild(container)
  }

  const mountDialogAndWait = async <T extends { new (...args: any[]): any }>(
    component: T,
    props: ExtractPropTypes<InstanceType<T>>,
    data?: any
  ) => {
    const confirmDialogReturn = useConfirmDialog()
    const extendedProps = { ...props, confirmDialogReturn }
    mountDialog(component, extendedProps)
    return await confirmDialogReturn.reveal(data)
  }
  return { mountDialog, mountDialogAndWait }
}

Currently the container div does not get removed from the DOM, because i don't know how to register for unmounted here. I tried already vNode.props?.onVnodeUnmounted but the vnode does not get unmounted. It's just the component which will be unmounted

I need somehow to register for the unmounted of the component. Anyone knows how to do this?


Solution

  • The unmount was not triggert for my component because there is no v-if on the main component which is getting mounted in the composable. I expose now a isVisible flag from all dialog components to trigger removing of the vnode.

      const mountDialog = <T extends { new (...args: any[]): any }>(
        component: T,
        props: ExtractPropTypes<InstanceType<T>>
      ) => {
        const container = document.createElement('div')
        const vNode = h(component, props)
        vNode.appContext = appContext
        render(vNode, container)
    
        if (vNode.component?.exposed?.isVisible === undefined) {
          throw new Error(
            'Dialog must expose isVisible otherwise unmount does not work!'
          )
        }
    
        const stopWatcher = watch(vNode.component?.exposed?.isVisible, () => {
          render(null, container)
          document.body.removeChild(container)
          stopWatcher()
        })
        document.body.appendChild(container)
      }