Reactivity API: Core
ref()
Takes an inner value and returns a reactive and mutable ref object. The ref object has a single property .value that points to the inner value.
Example:
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
If an object is assigned as a ref's value, the object is made deeply reactive by the reactive method.
Typing:
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<T>
Sometimes we may need to specify complex types for a ref's inner value. We can do that succinctly by passing a generics argument when calling ref to override the default inference:
const foo = ref<string | number>('foo') // foo's type: Ref<string | number>
foo.value = 123 // ok!
If the type of the generic is unknown, it's recommended to cast ref to Ref<T>:
function useState<State extends string>(initial: State) {
const state = ref(initial) as Ref<State> // state.value -> State extends string
return state
}
computed()
Takes a getter function and returns an immutable reactive ref object for the returned value from the getter.
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error
Alternatively, it can take an object with get and set functions to create a writable ref object.
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
Typing:
// read-only
function computed<T>(
getter: () => T,
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
// writable
function computed<T>(
options: {
get: () => T
set: (value: T) => void
},
debuggerOptions?: DebuggerOptions
): Ref<T>
interface DebuggerOptions {
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
interface DebuggerEvent {
effect: ReactiveEffect
target: any
type: OperationTypes
key: string | symbol | undefined
}
reactive()
Returns a reactive copy of the object.
const obj = reactive({ count: 0 })
The reactive conversion is "deep"—it affects all nested properties. In the ES2015 Proxy based implementation, the returned proxy is not equal to the original object. It is recommended to work exclusively with the reactive proxy and avoid relying on the original object.
Typing:
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
Note
reactive will unwrap all the deep refs, while maintaining the ref reactivity
const count = ref(1)
const obj = reactive({ count })
// ref will be unwrapped
console.log(obj.count === count.value) // true
// it will update `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2
// it will also update `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3
Important
When assigning a ref to a reactive property, that ref will be automatically unwrapped.
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
readonly()
Takes an object (reactive or plain) or a ref and returns a readonly proxy to the original. A readonly proxy is deep: any nested property accessed will be readonly as well.
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// works for reactivity tracking
console.log(copy.count)
})
// mutating original will trigger watchers relying on the copy
original.count++
// mutating the copy will fail and result in a warning
copy.count++ // warning!
As with reactive, if any property uses a ref it will be automatically unwrapped when it is accessed via the proxy:
const raw = {
count: ref(123)
}
const copy = readonly(raw)
console.log(raw.count.value) // 123
console.log(copy.count) // 123
watchEffect()
Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
Typing:
function watchEffect(
effect: (onInvalidate: InvalidateCbRegistrator) => void,
options?: WatchEffectOptions
): StopHandle
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // default: 'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
interface DebuggerEvent {
effect: ReactiveEffect
target: any
type: OperationTypes
key: string | symbol | undefined
}
type InvalidateCbRegistrator = (invalidate: () => void) => void
type StopHandle = () => void
See also: Watchers
watchPostEffect()
Alias of watchEffect with flush: 'post' option.
watchSyncEffect()
Alias of watchEffect with flush: 'sync' option.
watch()
The watch API is the exact equivalent of the Options API this.$watch (and the corresponding watch option). watch requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.
Compared to watchEffect,
watchallows us to:- Perform the side effect lazily;
- Be more specific about what state should trigger the watcher to re-run;
- Access both the previous and current value of the watched state.
Watching a Single Source
A watcher data source can either be a getter function that returns a value, or directly a ref:
// watching a getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// directly watching a ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
Watching Multiple Sources
A watcher can also watch multiple sources at the same time using an array:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
Shared Behavior with watchEffect
watch shares behavior with watchEffect. // TODO update links
Typing:
// watching single source
function watch<T>(
source: WatcherSource<T>,
callback: (
value: T,
oldValue: T,
onInvalidate: InvalidateCbRegistrator
) => void,
options?: WatchOptions
): StopHandle
// watching multiple sources
function watch<T extends WatcherSource<unknown>[]>(
sources: T
callback: (
values: MapSources<T>,
oldValues: MapSources<T>,
onInvalidate: InvalidateCbRegistrator
) => void,
options? : WatchOptions
): StopHandle
type WatcherSource<T> = Ref<T> | (() => T)
type MapSources<T> = {
[K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}
// see `watchEffect` typing for shared options
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // default: false
deep?: boolean
}
See also: Watchers