Computed properties and watchers are two of the most fundamental concepts in Vue.
But I often see people mixing them up — using one when they should be using the other.
They have a lot of similarities, so it can be hard knowing which one is best for what you're trying to accomplish.
In this article you'll learn:
When building components in Vue, we often need to respond to changes in our props.
A watcher — or watched prop — let's us track a property on our component and run a function whenever it changes.
For example, if the prop colour
changes, we can decide to log something to the console:
export default {name: 'ColourChange',props: ['colour'],watch: {colour()console.log('The colour has changed!');}}}
You can put a watcher on any reactive property. This includes computed props, props, as well as data that is specified inside of data()
on your Vue component.
They're really useful for creating side effects — things that don't update your application's state immediately.
If you need to fetch data or do some other asynchronous action, watched props are really good for that. Or maybe you need to interact with an imperative browser API, such as localstorage
.
Because watchers aren't expected to be pure functions, we can do all sorts of things like this with them!
Watched props are really powerful, but many times I accidentally use a watcher when all that I needed was a computed property.
Computed props let us compose new data from other data.
Let's say we have a component that takes a person's firstName
and lastName
. We can create fullName
as a computed prop:
export default {name: 'FullName',props: ['firstName', 'lastName'],computed: {fullName() {return this.firstName + ' ' + this.lastName;}}}
Because computed props are reactive, whenever firstName
or lastName
are changed, fullName
will be recomputed and will always be up to date.
Already we can see an advantage of computed props over watchers.
Composing this data together can be done with watchers, but it's much cleaner and more declarative using computed props:
export default {name: 'FullName',props: ['firstName', 'lastName'],data() {return {fullName: this.firstName + ' ' + this.lastName,};},watched: {firstName() {this.fullName = this.firstName + ' ' + this.lastName;},lastName() {this.fullName = this.firstName + ' ' + this.lastName;}}}
This is a fairly simple example — computed props can depend on as many other properties as you need them to. You can even watch other computed props, as well as reactive data in data()
!
Computed properties are required to be pure functions.
This means that they return a new value, but aren't allowed to change anything, and they must be synchronous.
Once we've created our computed prop, we can access it like we would any other prop. This is because computed props are reactive properties, along with regular props and data.
Now that we've covered how to use watchers and computed props, where do we use them?
The most common use case for a watched prop is in creating side effects.
A side effect is anything that affects state outside of the component, or affects state in an asynchronous way.
Common examples are:
None of these things affect your component directly, so they are considered to be side effects.
We'll look at an example of fetching data.
Let's say we have a MovieData
component, and it fetches data from the server based on what the movie
prop is set to:
export default {name: 'MovieData',props: {movie: {type: String,required: true,}},data() {return {movieData: {},}},watch: {// Whenever the movie prop changes, fetch new datamovie(movie) {// Fetch data about the moviefetch(`/${movie}`).then((data) => {this.movieData = data;});}}}
Now, this component will work wonderfully. Whenever we change the movie
prop, our watcher will fire, and it will fetch us the new data.
You can also use watchers to recompute data in certain cirumstances.
While computed props are generally the better way of doing this (as we'll see in a moment), there are cases where it doesn't make sense to use a computed prop.
NOTE: Be careful with using a
watch
to update state. This means that both your component and the parent component are updating — directly or indirectly — the same state. This can get very ugly very fast.
The main use case for computed props, as discussed before, is in composing data from other data.
This use case is pretty straightforward, but extremely useful. In my Vue code I use computed props far more than I use watchers, and almost always they are used to compose new data together.
I'll say this again because it's so important.
Using computed props to compose new data is one of the most useful patterns to learn when building Vue applications.
I use them literally everywhere.
Another one is allowing us easier access to nested data.
If we had some deeply nested data in our component that we needed access to, like data.nested.really.deeply.importantValue
, we can simplify this a whole lot:
computed() {importantValue() {return this.data.nested.really.deeply.importantValue;},}
And now we only need to write this.importantValue
to get ahold of it!
Let's see how they are different:
But they also have some similarities.
They aren't exactly twins, but they both:
But how do you know which one to use in a given situation?
Watched properties can often be confused with computed properties, because they operate in a similar way.
It's even trickier to know when to use which one.
But I've come up with a good rule of thumb.
Watch is for side effects. If you need to change state you want to use a computed prop instead.
Most of the time you'll want a computed prop, so try to use that first. If it doesn't work, or results in weird code that makes you feel like taking a shower, then you should switch to using a watched prop.
If you want to learn more about this, Sarah Drasner has a great article comparing computed props, watchers, and methods.
You can also check out the official documentation, which addresses this issue specifically.