Reactive State
By Syncfusion®, Inc. · Last updated
What reactivity is, <script setup> and top-level bindings, ref() and .value, unwrapping in templates, reactive() and proxies, the limits of reactive() and the destructuring problem, ref vs reactive, reactive arrays, Map and Set, DOM update timing with nextTick(), and typing state in TypeScript.
Reactivity
- Reactivity = when data changes, everything that uses it updates by itself.
- A spreadsheet works like this. Change one cell, and the total cell changes too.
- Vue brings this idea to web pages.
- You change the state. Vue updates the screen.
- But Vue only watches state you create in a special way.
Simple lesson: reactive state is data that Vue watches.
A Real-World Example
- Meera's page shows how many plates are in the cart.
- She tried a normal
letvariable. The number on screen never moved. - Then she used
ref(). Now the screen follows every click. - Run both buttons and compare.
- What happened: the plain variable did go up. The Console proves it.
- But the screen still shows 0, because Vue does not watch plain variables.
- The
refnumber updates on every click.
What You Will Learn
- How
<script setup>gives values to the template. - How to make state with
ref(). - When you need
.value, and when you do not. - How to make state with
reactive(). - The limits of
reactive(), and the destructuring trap. - When to choose
reforreactive. - How arrays,
MapandSetstay reactive. - When the screen really updates, and
nextTick(). - How to type state in TypeScript.
script setup
<script setup>is the code part of a component.- Every top-level name in it can be used in the template: variables, functions and imports.
- Top-level = written directly in the script, not inside a function.
- Imports work too. You can use an imported function straight in the template.
Simple lesson: if a name sits at the top of <script setup>, the template can use it.
ref()
ref(value)wraps a value in a small box that Vue watches.- The box has one property:
.value. - In the script, read and change it with
.value. - In the template, write the name only. Vue opens the box for you. This is called unwrapping.
- Why
const? The box never changes, only what is inside it. Soconst count = ref(0)and thencount.value++is correct.
Animation: a ref is a box holding a value. The script changes box.value from 0 to 1. Vue sees the change and the template shows 1
const orders = ref(0)
.value
0 1
orders.value++ Vue: "it changed!"
Orders: 0 Orders: 1
Script: orders.value. Template: just orders.
A ref is a box. Change what is inside with .value, and Vue updates the screen.
- What happened: the script used
.valueevery time. - The template used
customer,platesandorder.dishwith no.value. - A ref can hold an object too. Changes deep inside it are watched.
Template unwrapping only works for top-level refs. A ref stored inside a plain object, such as const box = { count: ref(1) }, is not unwrapped in an expression like {{ box.count + 1 }}. Keep refs at the top level.
Simple lesson: ref() makes a watched box. Use .value in the script, not in the template.
reactive()
reactive(object)makes a whole object reactive.- There is no
.value. You use the object's properties directly. - It works by wrapping the object in a proxy.
- Proxy = a stand-in object that notices every read and every change.
- Always change the object through the proxy that
reactive()returns. Changing the original plain object is not noticed.
Limits of reactive()
- It only works with objects, arrays,
MapandSet. Not with a plain number or text. - You cannot replace the whole object.
order = { ... }breaks the link with the screen. - Destructuring breaks reactivity.
let { plates } = ordercopies the number out of the proxy. - After that, changing
platesdoes not changeorderor the screen.
- What happened: "Add to the copy" changed only the copy. The Console shows it.
- "Add to the order" changed the real state, so the screen updated.
Need separate variables from a reactive object? Use toRefs(order). It turns each property into a ref that stays linked. You will meet it again in Advanced Reactivity.
Simple lesson: reactive() makes an object watched. Do not destructure it or replace it.
ref vs reactive
| Point | ref() | reactive() |
|---|---|---|
| Holds | Any value: number, text, object, array | Objects, arrays, Map, Set |
| In the script | count.value | order.plates |
| In the template | count | order.plates |
| Replace the whole value | Yes: list.value = [] | No |
| Destructure safely | Yes | No (use toRefs) |
| Official advice | Use as the main tool | Use for grouped values, carefully |
- The Vue docs advise
ref()as your main tool, because it has fewer traps. - This course uses
ref()most of the time.
Simple lesson: when in doubt, use ref().
Arrays, Map and Set
- Array methods such as
push,spliceandsortare watched. - Replacing the array with
list.value = [...]is watched too. - A reactive
Mapand a reactiveSetare watched as well. Setkeeps only unique values. Adding "spicy" twice keeps one "spicy".
Simple lesson: arrays, Maps and Sets stay reactive when you change them with their own methods.
When the Screen Updates
- Vue does not update the screen at the exact moment you change state.
- It waits until your function finishes, then updates once.
- This saves work when you change many values in a row.
- To read the screen after the update,
await nextTick().
- What happened: right after the change, the page still said "Closed".
- After
await nextTick(), the page said "Open".
Simple lesson: Vue updates the screen a moment later, all at once. nextTick() waits for it.
Typing State in TypeScript
refusually guesses the type from the starting value:ref(0)is a number.- Give the type yourself when the guess is not enough:
ref<string | null>(null). - For a list of objects, describe one object with an
interface. reactivetakes its type from the object, or from an interface you give it.- In the JavaScript flavour, the types are simply removed.
Real projects check these types with vue-tsc when you build. In this lab, types are removed before the code runs, and the editor does not check types in .vue files. So a wrong type does not stop the preview. Run vue-tsc in a real project to catch it.
Simple lesson: add types with ref<Type>() and interfaces. JavaScript just leaves them out.
TL;DR
- Reactivity = when state changes, the screen updates by itself.
<script setup>= every top-level name is usable in the template.ref(x)= a watched box. Use.valuein the script, not in the template.reactive(obj)= a watched object (a proxy). No.value.- Destructuring trap =
let { a } = reactiveObjcopies the value and loses reactivity. - ref vs reactive = prefer
ref(). It can be replaced and destructured safely. - Arrays, Map, Set = stay reactive when changed with their own methods.
nextTick()= waits until Vue has updated the screen.- Typing =
ref<Dish[]>([]),ref<Dish | null>(null).
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Plain let for state | Any variable updates the screen | Use ref() or reactive() | The value changes, the screen does not |
No .value in the script | count++ works on a ref | Write count.value++ | Nothing happens, or a type error |
Destructuring reactive() | let { plates } = order stays linked | Use order.plates or toRefs | Changes do not reach the screen |
| Replacing a reactive object | order = { ... } is fine | Change its properties, or use a ref | The screen keeps the old object |
reactive(0) | reactive works with any value | Use ref(0) for single values | A warning, and nothing is reactive |
| Reading the DOM right after a change | The screen updates at once | await nextTick() first | You read the old text |
A ref inside a plain object in {{ }} | All refs are unwrapped | Keep refs at the top level | You see [object Object] or wrong maths |
Check Yourself
Exercises
What to Remember
- Only
ref()andreactive()state updates the screen. - Refs use
.valuein the script. The template unwraps them. - Never destructure or replace a
reactive()object. - Prefer
ref(). It works for every kind of value. - Vue updates the screen after your code finishes.
nextTick()waits for it.
Next: Computed Properties and Watchers