DEV Community

Cover image for Use watch as the last resort
code plato
code plato

Posted on

Use watch as the last resort

We should avoid using watch in Vue. I'm not saying it's bad; we should just treat watch (and watchEffect) as a last resort. watch is a very reliable tool. It is the simplest and laziest way to achieve a function. It can almost guarantee 100% triggering, thus covering your needs. But the problem is that sometimes it covers too much. watch has the following disadvantages:

Disadvantages

1. Implicit Dependency

watch creates an implicit dependency.

An implicit dependency is a hidden relationship between components in a system where one component relies on another without being explicitly stated.

An explicit dependency occurs when component A directly calls component D, making the relationship clear and easy to trace.

Explicit Dependency

Implicit dependency means that component A eventually calls component D in an unobvious way, creating a connection between them.

Implicit dependency

Although explicit dependencies seem easier to maintain, a system full of them would lack flexibility. For example, vue-router is an implicit dependency, and implicit dependencies are essential for building frameworks. However, using them in business code increases maintenance difficulty.

Here is a great article that explains the disadvantages of implicit dependencies:

Side Effects in Code: Why They Matter and How to Avoid Them

Although this article does not explicitly mention implicit dependencies, side effects are essentially how watch operates in Vue.

Side effects create implicit dependencies between components, increasing the chance of bugs. For instance, in our discussion, I noted: "If a method assumes that a specific member variable will be updated beforehand, you’ve built a trap for the next developer — or even for yourself six months later."

2. Difficult to Trace the Source of Changes

When an error occurs, it is difficult to determine which piece of code initially triggered the change. If you are debugging in a local environment, it's easy to check the stack trace. But if you are troubleshooting on QA, CM, or PROD and are unaware of a watch, debugging can become a nightmare.

Image description

A real-world example: while working on one of the page of our project, we encountered a defect that seemed to originate from the data initialization. However, we didn't realize that the data were initialized twice—once via a watch. If both initializations worked correctly, the duplicate execution would go unnoticed. But when bugs inevitably appear, identifying the source of the error becomes critical in measuring code quality.

3. Creates Tight Coupling, Making Refactoring Difficult

Using watch can tightly couple different parts of the code. Initially, we knew that code A triggered code B through watch. Over time, additional code was introduced—code C also triggered code B through watch. Then, a third engineer, seeing the existing pattern, added code D, further expanding the web of dependencies. This results in increased coupling, making the code harder to maintain.

This article discusses the dangers of tight coupling:

Code Coupling: Reducing Dependency in Your Code

You’ve no doubt encountered this effect: you make a change in one corner of the system and it breaks code in a far, far distant corner. You might not even spot such a defect, particularly if you don’t do extensive regression testing with each release. That, of course, means your unfortunate customer will likely be the one who spots the defect.

A watch is like a spider web—it captures any logic that falls into its scope. If you watch a global variable, such as savedSession, its impact could extend across the entire project.

Image description

A project with excessive watch usage resembles the image above.

4. May Cause Infinite Loops

If watch A triggers watch B, and watch B triggers watch A, you create an infinite loop.

A real example: while developing QuotesPage.vue, I was a big fan of watch and added five or six of them on a single page. This led to an infinite loop when the page attempted a "relaxation" operation. At the time, there was no good way to suspend a watcher. See this answer from Evan You, the creator of Vue.js:

Image description

The only way to suspend a watch is to include a conditional check. I had to add some variables like watcherEnabled disable the watch when necessary. After I stopped using watch, I was able to say goodbye to this ugly workaround.

5. Hard to Read and Understand

When reviewing the logic behind a watch, you often need to navigate between multiple files or distant parts of the same file.

Image description

You might need to jump across different areas of the project to understand the full logic flow.

Better Solutions

There are better alternatives to watch. Consider the following before using a watcher:

  • Directly call the method if they are in the same component.
  • Use computed properties if you only need to display something.
  • Use custom events to trigger methods in a parent component.
  • Use lifecycle hooks like app:rendered, page:start, definePageMeta in Nuxt, or onMounted in Vue.

When to Use watch

Despite its drawbacks, watch is still useful in some cases:

  • When you need to update something, but custom events cannot achieve the desired behavior.

By using watch cautiously and considering alternatives first, you can write more maintainable and predictable Vue applications.

Top comments (0)