Advanced Patterns — Series 2

Preview — 3 of 10 questions

Why does itemCount.value show the updated number immediately, but listEl.value.children.length doesn't — until await nextTick() runs?

javascript
<script setup>
import { ref, nextTick } from 'vue'

const items = ref(['a', 'b'])
const itemCount = ref(items.value.length)
const listEl = ref(null)

async function addItem() {
  items.value.push('c')
  itemCount.value = items.value.length

  console.log(itemCount.value)               // 3
  console.log(listEl.value.children.length)   // 2 — still stale!

  await nextTick()
  console.log(listEl.value.children.length)   // 3 — now updated
}
</script>
AVue batches DOM updates and flushes them asynchronously on the microtask queue — reactive data (itemCount) updates synchronously, but the actual DOM patch happens later; nextTick() returns a promise that resolves right after that pending DOM update completes
BlistEl.value.children.length is cached and never updates until the page is refreshed
Cpush() doesn't trigger reactivity at all — itemCount.value only happened to be correct by coincidence
DnextTick() forces a full page reflow, which is the only way to read up-to-date DOM state in Vue

Why can useFeatureFlag() reliably read the instance when called at the top of setup(), but not when called inside setTimeout?

javascript
import { getCurrentInstance } from 'vue'

function useFeatureFlag(flagName) {
  const instance = getCurrentInstance()
  if (!instance) {
    console.warn('useFeatureFlag must be called inside setup()')
    return false
  }
  return instance.appContext.config.globalProperties.$flags?.[flagName] ?? false
}
AgetCurrentInstance() works anywhere, including inside a setTimeout callback fired long after setup() has finished
BgetCurrentInstance() is a public, officially documented API meant for use in application code — it's the recommended way to access any component's internal state
CgetCurrentInstance() only returns a non-null instance while a component's setup() is actively running synchronously (or during specific lifecycle hooks) — calling it after an await, or inside a callback that fires later (like this setTimeout), typically returns null
DgetCurrentInstance() throws a compile-time error if called inside a composable rather than directly inside a component's <script setup>

Why does mutating chart.value.data here NOT trigger a re-render?

javascript
<script setup>
import { ref, markRaw } from 'vue'
import { ChartLibrary } from 'heavy-chart-lib'

const chart = ref(markRaw(new ChartLibrary()))

function updateChart() {
  chart.value.data = [1, 2, 3] // does this update the template?
}
</script>
AmarkRaw() has no runtime effect — this is purely a TypeScript-only type hint
BmarkRaw() only affects reactive(), not ref() — wrapping in ref() re-enables full reactivity regardless
Cref(markRaw(...)) throws at runtime — ref() requires its argument to be reactivity-compatible
DmarkRaw() marks the object so Vue's reactivity system will never wrap it in a Proxy, even when it's later assigned into a ref/reactive() — mutations to its properties are invisible to Vue's dependency tracking, so no re-render is triggered

Sign up free to play

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