As you build your application, workflows often grow as you add features.
For example, a standard todo list starts with basic CRUD features.
import { ref } from 'vue'
const taskList = ref([])
function addTask() {
// Code to create a new task and add it to taskList
}
function updateTask() {
// Code to update the task label
}
function deleteTask() {
// Code to delete a task from taskList
}
However, as time goes on, you'll often want to add workflows (operations that run when state changes) such as saving to local storage whenever the taskList is updated.
The problem with manual workflow calls
A common example of this is wanting to sync your changes to local storage so that your state can persist between sessions. In practice, developers often follow this approach:
- Create a function like
saveToLocalStorage()that can be invoked whenever you want to save data. - Add it to any function that updates the target reactive state you want to save.
The code then becomes:
import { ref } from 'vue'
const taskList = ref([])
function addTask() {
// Code to create a new task and add it to taskList
saveToLocalStorage(taskList)
}
function updateTask() {
// Code to update the task label
saveToLocalStorage(taskList)
}
function deleteTask() {
// Code to delete a task from taskList
saveToLocalStorage(taskList)
}
As applications grow, this pattern becomes fragile. It's easy to forget to call the steps in a workflow in all of the functions that change state.
Automate workflows with watch
The principle to remember is that the state change is the single source of truth, not the functions that change it. Rather than attaching workflows to functions, you want to react to the state change directly with watch.
import { ref, watch } from 'vue'
const taskList = ref([])
watch(
taskList,
(newValue) => {
saveToLocalStorage(newValue)
},
{ deep: true }
)
function addTask() {
// Code to create a new task and add it to taskList
// ✅ Auto-saved by watch
}
function updateTask() {
// Code to update the task label
// ✅ Auto-saved by watch
}
function deleteTask() {
// Code to delete a task from taskList
// ✅ Auto-saved by watch
}
By doing it this way, you gain the benefits of:
- Centralizing your effects into a single source of truth that's easier to manage
- Cleaner functions since they only need to contain the logic that's relevant to that state
Summary
When you need to trigger workflows based on state changes, use watch instead of manually calling functions throughout your code. By attaching workflows directly to the reactive state they depend on, you create a single source of truth that automatically runs whenever that state changes. This approach keeps your functions focused on their primary responsibility while ensuring workflows run consistently, regardless of where or how the state is modified.