watch & Composables — Series 2

Preview — 3 of 10 questions

Why does this watcher fire when user.address.city changes, but not when user.address.zip changes?

javascript
import { reactive, watch } from 'vue'

const user = reactive({
  name: 'Ada',
  address: { city: 'London', zip: 'E1 6AN' },
})

watch(
  () => user.address.city,
  (newCity, oldCity) => {
    console.log(`City changed: ${oldCity}${newCity}`)
  }
)
AIt doesn't actually work — watch can't take a function as its source, only a ref or reactive object directly
BPassing a getter function as the source tells watch to track only the reactive dependencies read inside that function — here, only user.address.city is read, so only changes to that specific field trigger the callback; zip was never read by the getter, so it's simply not tracked
CThe getter function form watches the entire user object deeply — both city and zip changes should trigger it, this is a bug in the example
DGetter-function sources only work with refs, not properties nested inside a reactive() object

Given this setup, how many times does "Calculating..." get logged?

javascript
import { ref, computed } from 'vue'

const a = ref(1)
const b = ref(2)

const sum = computed(() => {
  console.log('Calculating...')
  return a.value + b.value
})

console.log(sum.value) // read #1
console.log(sum.value) // read #2 — same read, no change
a.value = 10
console.log(sum.value) // read #3 — a changed
AFour times — once per console.log(sum.value) call, plus once for the a.value = 10 assignment itself
BOnce — computed only ever evaluates its getter a single time, no matter how many times a dependency changes afterward
CTwice — once for the very first read (read #1), and once again after a.value = 10 changes a tracked dependency; read #2 reuses the cached result from read #1 since nothing changed between them
DThree times — once per console.log(sum.value) call, since reading .value always re-runs the getter

What's different about how this component reacts to state changes, compared to using reactive()?

javascript
import { shallowReactive } from 'vue'

const state = shallowReactive({
  user: { name: 'Ada', preferences: { theme: 'dark' } },
  loginCount: 0,
})

function renameUser() {
  state.user.name = 'Grace'          // (1) mutating a nested property
}

function incrementLogin() {
  state.loginCount++                  // (2) mutating a top-level primitive
}

function replaceUser() {
  state.user = { name: 'New', preferences: { theme: 'light' } } // (3) replacing the whole nested object
}
AshallowReactive() behaves identically to reactive() in every case — the "shallow" only affects refs inside the object, not plain nested objects
BNone of (1), (2), or (3) are reactive with shallowReactive() — only reactive() supports any of these mutations
CshallowReactive() makes every property reactive except functions — behaves the same as reactive() for all data properties
DOnly top-level properties are made reactive — mutating state.loginCount (2, a top-level primitive) triggers updates, and replacing state.user entirely (3, a top-level property reassignment) also triggers updates, but mutating a property nested inside state.user (1) does not, since state.user itself isn't converted into a reactive proxy

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.