Subscribe on changes!

feature: Ref Forwarding

avatar
Jan 12th 2021

Feature Request

I am requesting the addition of "ref forwarding" in Vue. In React, there is a concept of "ref forwarding" where a ref placed on the main React component can be forwarded to an inner element: https://reactjs.org/docs/forwarding-refs.html. My understanding is Vue does not currently support this: https://forum.vuejs.org/t/set-which-element-is-referenced-if-ref-is-set-from-parent/31480.

Use Case/Context

I am building a UI Library with Web Components and have added Vue 3 support. To do this, I have written Vue component wrappers that render out the underlying Web Component. For example, an IonContent Vue component would render out the underlying ion-content Web Component.

A problem arises when trying to call methods on the Vue component as the methods are defined on the Web Component, not the Vue component. Calling methods on the underlying Web Component requires developers to take the extra step of accessing $el:

<template>
  <ion-content ref="contentRef">...</ion-content>
</template>

// ✅ This is correct
contentRef.value.$el.scrollToBottom(); 

// ❌ This is incorrect and will result in an error.
contentRef.value.scrollToBottom(); 

This is confusing as developers can access methods on regular HTML elements and non-wrapper Vue components without $el.

Adding ref forwarding to Vue would greatly increase the user experience when wrapping Web Components with Vue as developers would no longer need to access $el before calling a method.

Alternatives

I could find a way to re-define the methods on the Vue component instance and proxy the arguments/results between the Vue component and Web Component, but that adds a lot of additional maintenance overhead and seems error-prone.

What does the proposed API look like?

Prior to knowing whether or not Vue supported ref forwarding, I originally checked to see if ref is part of the props object that is passed in to see if I could just re-assign it to the rendered Web Component:

const WrapperComponent = defineComponent((props) => {
  ...
  return () => {
    return h('ion-content', { ref: props.ref }); 
  }
});

/**
 * Now doing contentRef.value would give you the 
 * underlying ion-content Web Component.
 */

Perhaps we could do something similar? Not sure if it makes sense to expose the ref in the props object though.

avatar
Jan 13th 2021

This is confusing as developers can access methods on regular HTML elements and non-wrapper Vue components without $el.

It's a feature though, as accessing the component is often really useful. It's easy to write a function that unwraps the $el property:

function unwrapElement(el) {
  el = unref(el)
  return el instanceof HTMLElement ? el : el.$el
}

And then do

unwrapElement(contentRef).scrollToBottom()

I thought this was discussed in an RFC but couldn't find it. If you think this is still worth pursuing, it should go through the RFC process: https://github.com/vuejs/rfcs