Skip to content

Template refs don't work outside of setup() #6

@LachlanStuart

Description

@LachlanStuart

Related issue: vuejs/composition-api#91

The babel-sugar-setup-ref plugin only works inside the setup() function. This is problematic because when common logic is extracted into composable units in other functions, code can break silently, and without having any visible functional differences to working code.

E.g. this works:

export default createComponent({
    setup() {
        const visibilitySensorEl = ref(null);
        const isOnScreen = computed(() => visibilitySensorEl != null && isElementVisible(visibilitySensorEl));
        return () => (
            <div>
                <div ref={visibilitySensorEl} />
                {isOnScreen && renderContent()}
            </div>
        );
    }
})

But if you try to extract the logic, visibilitySensorEl stays null and nothing works:

const useVisibilitySensor = () => {
    const visibilitySensorEl = ref(null);
    return {
        renderVisibilitySensor: () => <div ref={visibilitySensorEl} />,
        isOnScreen: computed(() => visibilitySensorEl != null && isElementVisible(visibilitySensorEl))
    }
}
export default createComponent({
    setup() {
        const { renderVisibilitySensor, isOnScreen } = useVisibilitySensor();
        return () => (
            <div>
                {renderVisibilitySensor()}
                {isOnScreen && renderContent()}
            </div>
        );
    }
})

The big problem is that this difference is completely invisible to the developer - no compile errors, no console warnings, no obvious difference in code. This took me hours to debug, and I imagine plenty of other developers will also hit this case.

There are a few possible ways I can see this could be fixed:

  • Rewrite ref={something} everywhere, not just in the setup method
  • Emit a compile-time warning when ref={something} is seen outside of the setup method
  • Instead of detecting and modifying ref={} at compile time, wrap the return value of setup() with a function that walks the returned VNode tree and replaces ref values at run time
  • Instead of detecting and modifying ref={} at compile time, wrap h/createElement so that it automatically converts refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions