Avoid ruining the reactivity of reactive variables in Vue 3

ยท 2 mins read ยท ย Vue

Vue 3 has introduced two primary methods to create reactive variables. These are ref and reactive methods . Because of them, we can leave behind the beloved data function we have been using in Vue 2 to declare local state across our components.

# Vue 2 Component local state

Let's declare some local state in Vue 2 with the use of data function:

export default {
  data: () => ({
    form: {
      name: '',
      email: '',
    },
  }),
};

# Vue 3 Component local state

Now let's turn the above component to Vue 3 by using the reactive method:

import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      email: '',
    });

    return {
      form,
    };
  },
};

The reactive function takes an object and returns a reactive proxy of the original. It is equivalent to 2.x's Vue .observable().

# Vue 3 Component template

So far so good. Time to use the form attribute in our component's template:

<template>
  <form>
    <div>
      <label for="name">Name</label>
      <input id="name" v-model="form.name" />
    </div>

    <div>
      <label for="email">Email</label>
      <input id="email" v-model="form.email" />
    </div>

    <button>Submit</button>
  </form>
</template>

For both versions our component's template is exactly the same. The only thing we have to do is to map the correct tkey from form object with the correct DOM element and, we are done. Awesome!!

# Destructure reactive variables

So let's say that we need to simplify our template by not using nested keys when declaring v-model bindings in the markup. We refactor our component's template accordingly:

<template>
  <form>
    <div>
      <label for="name">Name</label>
      <input id="name" v-model="name" />
    </div>

    <div>
      <label for="email">Email</label>
      <input id="email" v-model="email" />
    </div>

    <button>Submit</button>
  </form>
</template>

How do we get access to name and email now? We could simply return form variable from setup method, since it is actually an object, right? No need to return form wrapped in an object anymore. Let's do so:

import { reactive } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      email: '',
    });

    return form;
  },
};

Unfortunately by doing so, we have just introduced a huge issue in our component since form variable just lost its reactivity. This means that v-model won't work as expected anymore, so we actually broke our code ๐Ÿ˜

# toRefs method to the rescue

Vue 3 has a hidden Ace for this pitfall by introducing toRefs method. This method actually saves the day for us since it is created exactly for this purpose.

By using it, we can return form variable from setup, wrapped with toRefs. That way, Vue 3 will create reactive references for each key of the form object, pointing to the corresponding property on the original object. Problem solved!!

import { reactive, toRefs } from 'vue';

export default {
  setup() {
    const form = reactive({
      name: '',
      email: '',
    });

    return toRefs(form);
  },
};

Newsletter

Get notified about latest posts and updates once a week!!