iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

reactive()

Vue reactivity in three lines: ref for primitives, reactive for objects, computed for derivations. Once these are reflex, the rest follows.

Vue — reactivity essentials

EXAMPLE
<script setup>
import { ref, reactive, computed, watch, watchEffect, isRef, toRefs } from 'vue';

// ===== ref(): primitive or single value =====
const count = ref(0);                 // .value on script side, auto-unwrapped in templates
count.value++;                        // mutate
console.log(count.value);             // read

// ===== reactive(): object/array (deep proxy) =====
const user = reactive({ name: 'Alex', age: 30, tags: ['vip'] });
user.age = 31;                        // tracked
user.tags.push('beta');               // tracked
// No .value here.

// ===== computed(): derived value, cached on deps =====
const greeting = computed(() => \`Hi ${user.name}\`);
const next = computed({
  get() { return count.value + 1; },
  set(v) { count.value = v - 1; },    // writable computed
});

// ===== watch(): explicit, lazy =====
watch(count, (newVal, oldVal) => {
  console.log('count changed', oldVal, '->', newVal);
});

// Watch multiple sources:
watch([count, () => user.name], ([c, n]) => console.log(c, n));

// Watch a reactive object deeply:
watch(user, (u) => console.log('user mutated', u), { deep: true });

// ===== watchEffect(): immediate, auto-tracks =====
watchEffect(() => {
  document.title = \`(${count.value}) ${user.name}\`;
});

// ===== Destructuring kills reactivity =====
const { name } = user;                // name is a plain string snapshot
const { name: nameRef } = toRefs(user); // nameRef is a real ref, stays reactive

// ===== Composables: reuse reactive logic =====
function useCounter(start = 0) {
  const value = ref(start);
  const inc = () => value.value++;
  const reset = () => value.value = start;
  return { value, inc, reset };
}
const counter = useCounter(10);

// ===== Helpers =====
isRef(count);                          // true
isRef(user);                           // false
</script>

<template>
  <p>{{ count }} — {{ greeting }}</p>
  <button @click="count++">Inc</button>
  <input v-model="user.name" />
</template>

<!-- ===== Patterns to internalise =====
- ref for primitives + single objects you intend to REPLACE
- reactive for tables/forms you intend to MUTATE in place
- computed for any derivation, including booleans driving v-if
- toRefs when destructuring or returning from a composable
- watchEffect for side effects, watch for explicit before/after

===== Pitfalls =====
- Destructuring reactive() -> plain values, no reactivity (use toRefs)
- Replacing a reactive() reference (state = newObj) -> consumers still see the old proxy
- Forgetting .value in script context (templates auto-unwrap)
- Deep watch on a huge object -> CPU; prefer specific getters
- Setting computed from inside its own getter -> infinite loop
-->

Why it matters

Vue reactivity is opinionated and small. ref + reactive + computed + watch covers 95% of UI state work. Destructure with toRefs, derive with computed, and watchEffect when the side effect needs to track its own deps. Internalise these and the rest of Vue feels like syntactic sugar on top.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { reactive } from 'vue';
const state = reactive({ count: 0, name: 'Ada' });
state.count++;
Try it Yourself »

Discussion

Loading…