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

John Au-Yeung - Jan 19 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

It lets us extract logic easily and 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .