Three common mistakes a React developer makes in Vue

Prasad Jayakumar
Vue.js Developers
Published in
4 min readApr 18, 2021

--

Photo by Sarah Kilian on Unsplash

Front-end developers, who know more than one JS framework have an advantage these days. Sometimes, the developer fluency on one “framework” (say React) gets carried on to the development work in another framework (say Vue).

I will be sharing one such experience of React developers working on Vue 2 based project. Some of the listed points would work in Vue, but that’s NOT the approach a hard-core Vue developer would have taken.

  1. Passing a function as props for event handling (callback)
  2. Ignoring the “Two-way” data binding — v-model
  3. Stumble over the reactive variables

💡I will extend the blog as and when I observe a pattern

  1. Passing a function as props for event handling (callback)

Wrong Approach — In the example below, handleChange function defined in parent component (ColorMe) has been passed as a prop to the child component (ColorPicker)

ColorPicker.vue<input type="color" :value="color" @input="onChange"/>
....
props: {
// 1.1: Common mistake by React Developers
onChange: {
type: Function,
default: () => {}
}
},
...
ColorMe.vue<!-- 1.2: Common mistake by React Developers -->
<ColorPicker :onChange="handleChange" />

Right Approach — In the example below, child component (ColorPicker) emits an event along with input values. Parent component (ColorMe) listens to the event and takes necessary actions.

ColorPicker.vue<input type="color" :value="color" @input="handleInput"/>
...
methods: {
handleInput(event) {
// 1.1 Cleaner approach followed by Vue developer
this.$emit("change", event.target.value);
}
}
ColorMe.vue<!-- 1.2 Cleaner approach followed by Vue developer -->
<ColorPicker @change="handleChange" />
ColorPicker Vue Component

2. Ignoring the “Two-way” data binding — v-model

Two-way data binding is not encouraged by most modern JS frameworks. Vue provides the “two-way” data binding using a shortcut for two one-way data binding 😄 via v-model (and sync)

  • Parent to Child communication happens through props (eg., value)
  • Child to Parent communication happens through emit events (eg., input)
Vue 2 sample code<ColorPicker v-model="bgColor" />is a short-cut for<ColorPicker :value="bgColor" @input="bgColor=$event.target.value"/>

💡 Vue 3 has a breaking change related to v-model — Refer the official guide. v-model now serves as a replacement to .sync modifier and allows us to have multiple v-models on the custom component. Changes for the better😄

Vue 3 sample codeColorPicker.vue
<input type="color" :value="modelValue" @input="handleInput"/>
...
props: {
modelValue: {
type: String,
default: "#ff0000"
}
},
methods: {
handleInput(event) {
// 2.1 Cleaner approach followed by Vue developer
this.$emit("update:modelValue", event.target.value);
}
}
ColorMe.vue
<!-- 2.2 Cleaner approach followed by Vue developer -->
<ColorPicker v-model="bgColor" />

3. Stumble over the reactive variables

Reactive variables are extremely effective and helpful. There are few nitty-gritty to be aware.

For detailed explanation, let’s pick the following use-case (only mock)

  • Weather Component (Parent) — Allows to add city
  • WeatherList Component (Child) — Gets the weather details of all cities and render
Weather Component

Common pattern to map props and enrich the data is as following

created() {
this.fetchData(this.id)
},
watch: {
id: fetchData
},
methods: {
fetchData(id) {
// ...
}
}

For knowing reactivity in depth, I have written the logic using just Watch.

WeatherList (Child Component) - Vue 2 sample codedata() {
return {
weatherData: {},
};
},
watch: {
cities: {
// With immediate being true,
// you don't need to call the handler logic from mounted/created
immediate: true,
handler() {
this.cities.forEach((city) => {
// Non Reactive - common mistake a React developer does
// weatherData has been declared as an empty object
// In Vue 2, use $set to make the new key/value reactive

// this.weatherData[city] = Math.floor(Math.random() * 45);
// Reactive
if (!this.weatherData[city]) {
// Vue 2 caveat.
// $set is not available and not required in Vue 3

this.$set(this.weatherData, city, Math.floor(Math.random() * 45));
}
});
},
},
....

Vue 3 has an improved Reactivity logic — we don’t need to use $set. We need to use deep: true due to breaking change related to Watch on Arrays in Vue 3

data() {
return {
weatherData: {}
};
},
watch: {
cities: {
immediate: true,
deep: true, // required due to breaking change in Vue 3
handler() {
this.cities.forEach((city) => {
if (!this.weatherData[city])
this.weatherData[city] = Math.floor(Math.random() * 45);
});
}
}
}

There are several changes and breaking changes occurred between Vue 2 and 3 related to Reactivity. For more information about the changes, read the below RFC

--

--