Create Vue 3 Apps with the Composition API — Custom Refs and Shallow Reactivity

Photo by Jaunathan Gagnon on Unsplash

It lets us extract logic easily an not have to worry about the value of this in our code.

It also works better with TypeScript because the value of this no longer has to be typed.

In this article, we’ll look at how to create Vue 3 apps with the Composition API.

customRef

We can use the customRef function to create a custom ref.

For instance, we can write:

<template>
<div>
<input v-model="text" />
{{ text }}
</div>
</template>
<script>
import { customRef } from "vue";
const useDebouncedRef = (value, delay = 200) => {
let timeout;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
value = newValue;
trigger();
}, delay);
},
};
});
};
export default {
name: "App",
setup() {
return {
text: useDebouncedRef("hello world"),
};
},
};
</script>

We create the useDebounceRef function that returns a custom ref created bu the customRef function.

It takes a callback with the track function to track the value.

And the setter calls trigger to trigger state updates.

We ser the value in the setter to newValue so that the items are updated.

markRaw

We can call the markRaw function to mark an object so that it’ll never be converted to a proxy.

For instance, we can write:

<template>
<div></div>
</template>
<script>
import { isReactive, markRaw, reactive } from "vue";
export default {
name: "App",
setup() {
const foo = markRaw({});
console.log(isReactive(reactive(foo)));
return { foo };
},
};
</script>

The console log logs false since we called markRaw to disable reactivity on the object we passed into it.

But if we mark one object as raw and we reference that object in another object, then they’ll be considered not to be equal even though they have the same reference:

<template>
<div></div>
</template>
<script>
import { isReactive, markRaw, reactive } from "vue";
export default {
name: "App",
setup() {
const foo = markRaw({
nested: {},
});
    const bar = reactive({
nested: foo.nested,
});
    console.log(foo.nested === bar.nested);
return { foo };
},
};
</script>

We set bar.nested to foo.nested , but the console log is false .

shallowReactive

We can create a shallow reactive object with the shallowReactive function.

The reactive property is limited to the top-level properties of the object.

For instance, we can write:

<template>
<div>
<button @click="increment">increment</button>
{{ state.foo }}
{{ state.nested.bar }}
</div>
</template>
<script>
import { isReactive, shallowReactive } from "vue";
export default {
name: "App",
setup() {
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
});
    const increment = () => {
state.foo++;
state.nested.bar++;
};
console.log(isReactive(state.nested.bar));
return { state, increment };
},
};
</script>

In the increment function, we increment both state.foo and state.nested.bar and they’ll both update in the template.

But when we log state.nested.bar with isReactive , we get false logged.

Since it’s not reactive, it won’t trigger watchers to run:

<template>
<div>
<button @click="increment">increment</button>
{{ state.foo }}
{{ state.nested.bar }}
</div>
</template>
<script>
import { isReactive, shallowReactive, watch } from "vue";
export default {
name: "App",
setup() {
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
});
    const increment = () => {
state.foo++;
state.nested.bar++;
};
    watch(state.nested.bar, (bar) => {
console.log(bar);
});
    return { state, increment };
},
};
</script>

Then watch callback in the 2nd argument won’t run when state.nested.bar is updated.

Conclusion

We can use various functions provided Vue 3’s composition API to create various kinds of reactive and non-reactive properties.

More content at plainenglish.io


Create Vue 3 Apps with the Composition API — Custom Refs and Shallow Reactivity was originally published in JavaScript in Plain English on Medium, where people are continuing the conversation by highlighting and responding to this story.