2 Ways to Use Reactive With Vue 3
written by... Brian Driscoll | Date: February 24, 2023
Using ref or reactive and be confusing when learning Vue’s composition API.
As a good rule of thumb, use ref UNTIL you find a need for using reactive. But you might be wondering, “okay, I will use ref then… but when will I need to use reactive?”
In this article, I am going to show you two use cases when reactive is a better fit instead of ref.
What you will learn...
As always, you can watch the video, read the article, or use both!
Example 1: Tax Calculator
Use Reactive for Logically Grouping Information
Having tons of ref variables can be confusing. Instead, we can use reactive to make it explicit what the data is for.
In this example we are going to create a simple tax calculator. It will take in userData and output taxInfo.
Template:
<template> <h1> Tax Calculator </h1> <label> Name </label> <input v-model="userInput.name" /> <label> Address </label> <input v-model="userInput.address" /> <label> Total Income </label> <input v-model="userInput.totalIncome" /> <label> Employer </label> <input v-model="userInput.employer" /> <ul> <li> Name: {{ taxInfo.name }}</li> <li> Address: {{ taxInfo.address }}</li> <li> Total Income: {{ taxInfo.totalIncome }}</li> <li> Employer: {{ taxInfo.employer }}</li> <li> Total Tax: {{ taxInfo.totalTax }}</li> <li> Tax Rate: {{ taxInfo.taxRate }}</li> </ul> <button @click="submitInfo"> Submit </button></template>
Three things are happening:
- We create four inputs that are connected to userInput and a specific property within userInput (name, address, total income, employer).
- We create an unordered list to display tax info from taxInfo.
- We create a button with a click handler to submit information.
Now let’s take a look at the script:
<script setup>import { reactive } from "vue"const userInput = reactive({ name: "", address: "", totalIncome: 0, employer: "",})const taxInfo = reactive({ name: "", address: "", totalIncome: 0, employer: "", totalTax: 0, taxRate: ""})function submitInfo() { taxInfo.name = userInput.name; taxInfo.address = userInput.address; taxInfo.totalIncome = userInput.totalIncome taxInfo.employer = userInput.employer; if (userInput.totalIncome > 20000) { taxInfo.totalTax = userInput.totalIncome * 0.15 taxInfo.taxRate = "15%" } else { taxInfo.totalTax = userInput.totalIncome * 0.10 taxInfo.taxRate = "10%" }}</script>
Let’s take it step by step:
- userInput is declared as a reactive variable with four properties: name, address, totalIncome, employer. This variable will accept the user input via v-model (which is connected to the inputs on the template). 2.taxInfo is declared as a reactive variable with six properties. All the same properties userInput has but two more: totalTax and taxRate. You will see why in the next point.
- The function submitInfo is created and it takes the userInput and fills in the properties of taxInfo. But we have two properties left that aren’t filled: totalTax and taxRate.
- In submitInfo, we have an if-else statement that looks at the user’s income. Depending on their income, it uses different calculations to input how much the totalTax will be and the taxRate.
Why Reactive is Better for UserInput and Outputting Template Data
Instead of having 10 different ref variables, we have 2 reactive variables that are clearly dilineated.
(input) userInput - accepts user input
(output) taxInfo - takes in the information from userInput, has 2 other inputs that are calculated using a function, and then outputs the information to the template.
Example 2: Pokemon Display
Use Reactive for Receiving API Data
Usually, when you call an API you will be given a set of different data within an object. Since a get request is asking for something specific, like in this case we are going to be asking for a type of pokemon, the data is connected together logically.
For example, if I am looking for the pokemon “Charmander”, I might receive data for an image, skills, color type, and more of that specific pokemon.
Let’s build a simple pokemon API application to show you, first by starting with the template:
<template> <div class="container"> <h1> Pokemon Index </h1> <select v-model="pokemonName"> <option> bulbasaur </option> <option> ditto </option> <option> charmander </option> <option> squirtle </option> <option> blastoise </option> </select> <div class="headerContainer"> <h3> <p> Name: {{ pokemon.name }} </p> </h3> <img :src="pokemon.img_url" class="image"/> </div> <p> Skills: </p> <ul> <li v-for="(item, name) in pokemon.abilities"> {{ item }}</li> </ul> </div></template>
Let’s break this down step by step:
- We create a select input and connect it to a variable called pokemonName.
- We list a set of options inside this select input. The v-model will automatically update the pokemonName variable depending on what option we pick.
- We create a h3 header container that shows the pokemon name (pokemon.name) and an image using a URL (pokemon.img_url).
- We create a v-for loop within an unordered list to show the abilities of this pokemon.
Now let’s write the script:
<script setup>import axios from "axios";import { watch } from "vue";import { reactive, ref } from "vue";let pokemonName = ref("bulbasaur")let pokemon = reactive({ name: "", img_url: "", abilities: []})function fillData(data) { pokemon.name = capitalizeFirstLetter(data.name); pokemon.img_url = data.sprites.front_default; pokemon.abilities = []; console.log(data); for (let item in data.abilities) { pokemon.abilities.push(data.abilities[item].ability.name); }}function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1)}axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemonName.value}`) .then(res => fillData(res.data));watch(pokemonName, async (newPokemonName) => { await axios.get(`https://pokeapi.co/api/v2/pokemon/${newPokemonName}`) .then(res => fillData(res.data));})</script>
It may seem like quite a lot, but each part on its own is simple:
- We declare pokemonName as a ref variable that’s already populated with an entry: “bulbasuar”. This means the v-model in the select input will start off with a value.
- We declare a reactive variable called pokemon to store the pokemon’s name, image url, and abilities.
- We create a function called fillData. This function takes the data in as a parameter (which we will get using an axios get request).
- pokemon.name receives the name (data.name)from the API. We also insert the name into the function capitalizeFirstLetter to capitalize the first letter of the string.
- pokemon.img_url receives the sprite image link.
- pokemon.abilities is a bit odd. We declare it an empty array. This is because we create a for loop to push data into the array. If we didn’t reset the array to be empty, we would just keep adding skills. This way allows us to delete the previous skills from other pokemon and only add the skills of the corresponding pokemon.
- The for loop searches each item in data.abilities and pushes it into the pokemon.abilities array.
- We create the function capitalizeFirstLetter that accepts a string for the parameter. We take the first character of the string, capitalize it, then concatenate it to the rest of the word. For example, let’s take the string, charmander. First we take c then make it into C using .toUpperCase(). Then we slice the string from the 1st position (array positions start at 0), which gets us the rest of the word except for the first letter. We add C and harmander (C + harmander) together and get Charmander and return it.
- We create an axios get call to the API and insert the pokemon’s name using a template literal ${pokemonName.value}. This let’s us populate the data with an initial call of “bulbasaur”.
- We create a watch function that watches the ref variable pokemonName. Everytime the select input changes, so does pokemonName. This will trigger the watch function to run the axios.get request inside which also makes an API call. It inserts the new value of pokemonName via newPokemonName, and inserts that data into fillData to fill out the reactive variable pokemon.
Why Reactive is Good for Filling Data
We are able to separate out the use case of the ref variable and the reactive variable for this application.
(input) Ref - This was used to trigger api calls AND to insert information into those API calls.
(output) Reactive - This was used to take in the data from those API calls in the function fillData and display it in the template.
By using these two different functions, we can better understand what’s happening in our application by separating their roles.
Technically you could just use refs or reactive, but that would be repetitive and confusing allowing for no logical separation for the developer.
Conclusion
If you enjoyed the article, you may enjoy our free course. Click the link to sign up and check it out!
In this article you learned 2 use cases for when reactive is a superior choice than ref. You also learned how to logically separate the uses of your variables so you can simplify the data flow of your application.
I hope this article helped you improve your understanding of Ref and Reactive in the Vue framework.