Accessing Vue Component Data with $ref.

Accessing Vue Component Data with $ref.

$ref — Vue’s emergency hatch for direct component access.

This article assumes you have basic knowledge of Vue & Vue’s reactive system.

Emergencies are a given, and sometimes we get one of those when building frontend applications; thankfully, most of these frontend frameworks provide us with multiple ways to handle emergencies. With Vue, one of the many emergency hatches is the $ref attribute.

Generally, having a ref attribute on pure HTML elements (like the <input /> element) in Vue is common. In the same way, you can have a reference on a custom component as well (<my-custom-componet />), and have access to its computed values, methods, data properties, and so on. This, however, should only be used in emergencies or as a last resort.


Accessing Data — The General Approach

Say we have two child components (Component A & Component B) within a parent component, and we would, for some reason, need to display some data from Component A in B and vice-versa. See a rough sketch below:

General approach

The general and recommended approach would be to emit the data from A, add a listener/handler in the parent component, then pass the value into Component B via props. This would look something like this:

Handler

In some cases, we might need to hack our way around and eliminate some steps from this flow to make the data-passing trip slightly shorter. Say ComponentA wouldn't need to emit its data value to the parent component, then we can remove the emit and listen to steps, and directly access the values in ComponentA from the Parent Component.

Sharing data


Using $ref

Say we have two identical components — Component A & Component B.

Component A has two methods; setThought that sets the value of a thought data property to any value passed from the editable div, and another technique — readMind that does nothing for now.

<template>
  <div>
    <div contenteditable @input="setThought($event.target.innerText)">
      {{ thought }}
    </div>
    <div>
      <button @click="readMind">What is B thinking?</button> {{ otherThought }}
    </div>
  </div>
</template>

<script>
export default {
  name: "ComponentA",
  data() {
    return {
      thought: 'I should steal a cookie',
      otherThought: '',
    };
  },
  methods: {
    setThought(value) {
      this.thought = value;
    },
    readMind() { }
  },
};
</script>

Component B is similar, with just a slight difference in content:

<template>
  <div>
    <div contenteditable @input="setThought($event.target.innerText)">
      {{ thought }}
    </div>
    <div class="">
      <button @click="readMind">What is A thinking?</button> {{ otherThought }}
    </div>
  </div>
</template>

<script>
export default {
  name: "ComponentB",
  data() {
    return {
      thought: 'I like school a tiny bit',
      otherThought: '',
    };
  },
  methods: {
    setThought(value) {
      this.thought = value
    },
    readMind() { }
  },
};
</script>

You may or may not have already figured out what we want to do here. We need Component A to read Component B's thoughts without Component B emitting its thought.

For this, both components must have something in common - their parent. To share data between two components using $ref , they need to be children of the same parent component. So in a parent component, we'll import Component A and B into a parent component and assign ref attributes to both.

<template>
  <div id="app">
    <ComponentA ref="componentA" />
    <ComponentB ref="componentB" />
  </div>
</template>

<script>
import ComponentA from './components/ComponentA.vue'
import ComponentB from './components/ComponentB.vue'

export default {
  name: 'App',
  components: {
    ComponentA,
    ComponentB
  },
}
</script>

With this structure, we can easily access each Component by reference from its parent like so:

this.$parent.$refs.componentA

OR

this.$parent.$refs.componentB

Now we can update the readMind method for Component A so that on button-click, Component A would know exactly what Component B is thinking:

<script>
  export default {
    name: "ComponentA",
    methods: {
      ...
      readMind() {
        this.otherThought = this.$parent.$refs.componentB.thought
      }
      ...
    }
  }
</script>

Notice we can access the thought data property in ComponentB by setting a ref attribute on it and accessing it from its parent.

We can make a similar update to the readMind method in Component B to do the same thing - find out what Component A is thinking.

<script>
  export default {
    name: "ComponentB",
    methods: {
      ...
      readMind() {
        this.otherThought = this.$parent.$refs.componentA.thought
      }
      ...
    }
  }
</script>

What does this look like?

Preview


Can we set component values too?

Sure, just like the 2010 movie — Inception, let's force Component B's thoughts to be precisely what Component A thinks about. We can set the value of the data properties in the same way:

<script>
  export default {
    name: "ComponentA",
    methods: {
      ...
      setThought(value) {
        this.thought = value;
        this.$parent.$refs.componentB.thought = value;
      },
      ...
    }
  }
</script>

Alternatively, you can call the setThought method of Component B from Component A:

<script>
  export default {
    name: "ComponentA",
    methods: {
      ...
      setThought(value) {
        this.thought = value;
        this.$parent.$refs.componentB.setThought(value);
      },
      ...
    }
  }
</script>

What would this look like?

image


Oh, refs are reactive?

No, they're not. What you see is only a result of calling the setThought method every time there's an input in Component A and, in turn, setting the value of this.$parent.$refs.componentB.thought to the same value as the thought data property in ComponentA.


Refs are one of Vue's quirky parts and can get troublesome if you do not adequately understand their usage. The safest way to use them would be outside your lifecycle hooks and only within methods. Also, avoid direct usage within components' templates or computed properties.

If you'd like to fiddle around with the source code, you'll find it here: Repository.

Also, Here's a detailed guide on how and where to safely access refs, and of course, the official documentation.

Cheers ☕️