Skip to content
76 changes: 75 additions & 1 deletion src/guide/reactivity-computed-watchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ An async function implicitly returns a Promise, but the cleanup function needs t
Vue's reactivity system buffers invalidated effects and flushes them asynchronously to avoid unnecessary duplicate invocation when there are many state mutations happening in the same "tick". Internally, a component's `update` function is also a watched effect. When a user effect is queued, it is by default invoked **before** all component `update` effects:

```html

<template>
<div>{{ count }}</div>
</template>
Expand Down Expand Up @@ -201,7 +202,7 @@ A watcher can also watch multiple sources at the same time using an array:

```js
const firstName = ref('');
const lastName= ref('');
const lastName = ref('');

watch([firstName, lastName], (newValues, prevValues) => {
console.log(newValues, prevValues);
Expand All @@ -211,6 +212,79 @@ firstName.value = "John"; // logs: ["John",""] ["", ""]
lastName.value = "Smith"; // logs: ["John", "Smith"] ["John", ""]
```

### Watching Reactive Objects

Using a watcher to compare values of an Array or Object that are reactive requires that it has a copy made of just the values.

```js
const numbers = reactive([1, 2, 3, 4])

watch(
() => [...numbers],
(numbers, prevNumbers) => {
console.log(numbers, prevNumbers);
})

numbers.push(5) // logs: [1,2,3,4,5] [1,2,3,4]
```

Attempting to check for changes of properties in a deeply nested Object or Array will still require the `deep` option to be true, if watching an Object or Array.

```js
const state = reactive([
{ attributes: { name: "" }, id: 1 },
{ attributes: { name: "" }, id: 2 },
]);

watch(
() => state.map((e) => e.attributes),
(attrs, prevAttrs) => {
console.log(
'not deep ',
attrs.map((e) => e.name),
prevAttrs.map((e) => e.name)
);
}
);

watch(
() => state.map((e) => e.attributes),
(attrs, prevAttrs) => {
console.log(
'deep ',
attrs.map((e) => e.name),
prevAttrs.map((e) => e.name)
);
},
{ deep: true }
)

state[0].attributes.name = "Alex"; // Logs: "deep " ["Alex", ""] ["Alex", ""]
```

However, watching a reactive Object or Array will always return a reference to the current value of that Object. To fully watch deeply nested Objects and Arrays, a deep copy of values may be required. This can be achieved with a utility such as [lodash.cloneDeep](https://lodash.com/docs/4.17.15#cloneDeep)

```js
import _ from 'lodash';

const state = reactive([
{ attributes: { name: "" }, id: 1 },
{ attributes: { name: "" }, id: 2 },
]);

watch(
() => state.map((e) => (_.cloneDeep(e.attributes))),
(attrs, prevAttrs) => {
console.log(
attrs.map((e) => e.name),
prevAttrs.map((e) => e.name)
);
}
);

state[0].attributes.name = "Alex"; // Logs: ["Alex", ""] ["", ""]
```

### Shared Behavior with `watchEffect`

`watch` shares behavior with [`watchEffect`](#watcheffect) in terms of [manual stoppage](#stopping-the-watcher), [side effect invalidation](#side-effect-invalidation) (with `onInvalidate` passed to the callback as the 3rd argument instead), [flush timing](#effect-flush-timing) and [debugging](#watcher-debugging).