Reactivity is a programming paradigm in which changes to data automatically trigger updates in the UI. Under the hood, requery-js uses Vue's reactivity system to make this possible.
Written By Joel
Last updated About 1 month ago
What is Reactivity?
A great way to understand reactivity is through a familiar example: a spreadsheet. Let's look at a simple requery-js component that implements spreadsheet-like behaviour:
In the example below, cell A3
is defined using the formula = A1 + A2
. If you update A1
or A2
, you’ll notice that A3
automatically updates.
Vanilla JavaScript
Let's try to recreate our spreadsheet behaviour in plain JavaScript.
let a1 = 1
let a2 = 2
let a3 = a1 + a2
console.log(a3) // 3
a1 = 2
console.log(a3) // Still 3!
When we change a1
, a3
doesn't automatically update. To make this work, we'd need to wrap the calculation in a function:
let a1 = 1
let a2 = 2
let a3
function update() {
a3 = a1 + a2
}
// Initial calculation
update()
console.log(a3) // 3
// Manually recalculate after changes
a1 = 2
update()
console.log(a3) // 4
This manual update approach quickly becomes unmaintainable in real applications. As your project grows, you need to track all the places where values are used and ensure updates are called at the right time. Miss a single update and your UI will be out of sync with your data.
How Reactivity Works in requery-js
Let's look at how reactivity is implemented with a simplified version of our spreadsheet component:
defineComponent("rq-spreadsheet", {
// 1. Create reactive state
store: {
a1: 1,
a2: 2
},
setup(component, props, store) {
// 2. Bind the display value
component.query("cell-a3").text(() => store.a1 + store.a2);
}
});
What happens under the hood?
Creating Reactive State: The
store
object becomes reactive, allowing any access to its properties to be tracked.Tracking Dependencies: The text function runs for the first time, recording that it depends on
store.a1
andstore.a2
.Automatic Updates: When a value changes:
The change to the reactive property is detected
All dependent effects are found
Those effects re-run to update the UI
This means you can focus on describing what your UI should look like based on your data, and everything stays in sync automatically.
Deep Reactivity
The store is deeply reactive by default. This means changes to nested objects and arrays will also trigger updates:
defineComponent("rq-spreadsheet", {
store: {
cells: {
"A": [1, 2],
"B": [0, 0],
"C": [0, 0]
}
},
setup(component, props, store) {
// Child property mutations will trigger reactive updates
store.cells["A"][0] = 40
}
})
Creating Reactive State Outside Components
While most reactive state lives inside component stores, you can create standalone reactive state using Vue's reactive()
function:
import {
reactive
} from 'https://esm.run/requery-js@0.2'
const state = reactive({
count: 0,
})
// State changes will trigger updates
state.count++
Additional Reactivity APIs
The following APIs are exported from @vue/reactivity and provide additional ways to work with reactive state:
ref()
Creates a reactive reference holding a single value. Unlike reactive objects, refs need to be accessed using .value
. This is particularly useful for primitive values like numbers or strings:
import {
ref
} from 'https://esm.run/requery-js@0.2'
const count = ref(0)
// Access or change the value using .value
console.log(count.value) // 0
count.value++
toRaw()
Gets the raw, original object from a reactive proxy. This is useful when you need to access the underlying data without triggering reactive effects:
import {
reactive,
toRaw
} from 'https://esm.run/requery-js@0.2'
const state = reactive({
count: 0
})
const rawState = toRaw(state)
rawState.count++ // This won't trigger reactive updates
watch()
Watches reactive state and runs a callback when it changes. While you typically won't need this in requery-js components, it's useful for advanced use-cases:
import {
reactive,
watch
} from 'https://esm.run/requery-js@0.2'
const state = reactive({
count: 0
})
watch(
() => state.count,
(newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`)
}
)
effect()
Creates a reactive effect that automatically re-runs when its dependencies change. Like watch
, this is more commonly used outside of requery-js components:
import {
reactive,
effect
} from 'https://esm.run/requery-js@0.2'
const state = reactive({
count: 0
})
effect(() => {
console.log(`Count is: ${state.count}`)
}) // Logs: "Count is: 0"
state.count++ // Logs: "Count is: 1"
Note
While these @vue/reactivity APIs are available, you typically won't need them directly in components since requery-js's binding system handles reactivity automatically. They're more useful for advanced use cases or when working with reactive state outside of components.
Could not load article.
Could not load article.