[Vue Pattern] Flexible arguments
This course is based on our Coding Better Composables blog series authored by Michael Thiessen.
When using composables in Vue.js, sometimes you already have a ref that you want to use. Other times, you don’t. This lesson will go through a pattern that lets you use your composables either way, giving you more flexibility when writing your applications.
What are flexible arguments?
Almost all composables require some type of argument as an input. Often, this is a reactive ref
. It can also be a primitive Javascript type, like a string, number, or object. But we want to write our composables to be even more flexible and reusable, right?
Instead of requiring either a ref or a primitive, we can accept either. The useTitle
composable that we saw in the previous article applies the flexible argument pattern.
When you pass in a ref, it’s linked to the document title. Then the title will be set to the value of that ref
:
const title = ref('This is the title');
useTitle(title);
title.value = 'New title please';
If you pass in just a string, it will create a new ref
for you and then proceed to link it up to the document title:
const title = useTitle('This is the title');
title.value = 'New title please';
In these contrived examples, it doesn’t look like much of a difference. However, when you’re using other methods and composables, you might already have a ref
from somewhere else. Or you might not. Either way, this composable can adapt to what you need.
Now let’s see how to make this work in our composables.
Implementing flexible arguments in a composable
To make the flexible arguments pattern work, we need to use either the ref
function or the unref
function on the argument we get:
// When we need to use a ref in the composable
export default useMyComposable(input) {
const ref = ref(input);
}
// When we need to use a raw value in the composable
export default useMyComposable(input) {
const rawValue = unref(input);
}
The ref
function will create a new ref
for us. But if we pass it a ref
, it just returns that ref
to us:
// Create a new refconst myRef = ref(0);
// Get the same ref back
assert(myRef === ref(myRef));
The unref
function works the same, but instead it either unwraps a ref
or gives us our primitive value back:
// Unwrap to get the inner valueconst value = unref(myRef);
// Returns the same primitive value
assert(value === unref(value));
Let’s see how some composables from VueUse implement this pattern. VueUse is an open-source collection of composables for Vue 3 and is very well written. It’s a great resource to learn how to write great composables!
useTitle
We’ll come back to the useTitle
composable since we’re already familiar with it.
This composable lets us pass in either a string or a ref
of a string. It doesn’t care which we provide:
// Pass in a string
const titleRef = useTitle('Initial title');
// Pass in a ref of a string
const titleRef = ref('Initial title');
useTitle(titleRef);
In the source code, you can see that right after we destructure our options object, we create the title
ref. We use the ref
function here, which allows us to use either a ref
or a string to make the title
ref:
// ...
const title = ref(newTitle ?? document?.title ?? null)
// ...
The ??
syntax is the nullish coalescing operator — a fancy-sounding name for “if the value on the left is null or undefined, use the value on the right.” So this line first tries to use newTitle
, but if that isn’t defined, it will use document.title
, and if that isn’t defined, it will give up and use null
.
Something interesting to note for you TypeScript connoisseurs:
The newTitle
variable used here has the type MaybeRef<string>
. Here is what the type is defined as:
type MaybeRef<T> = T | Ref<T>
This type definition means that the type MaybeRef<string>
can either be a string
or a Ref<string>
, which is a ref with a string value inside.
The next composable we’ll look at also uses this type to implement this pattern.
useCssVar
The useCssVar composable allows us to grab the value of a CSS variable and use it in our app:
const backgroundColor = useCssVar('--background-color');
Unlike useTitle
though, here we need the string value so that we can look up the CSS variable in the DOM. Using the unref
function, this composable can handle both refs and strings being passed in:
// Using a string
const backgroundColor = useCssVar('--background-color');
// Using a ref
const cssVarRef = ref('--background-color');
const backgroundColor = useCssVar(cssVarRef);
Looking at the source code, we can see that it uses the unref
function to accomplish this. Actually, it uses a helper function, called unrefElement
, to ensure we’re getting a DOM element and not just a Vue instance.
Most composables in VueUse implement this pattern, if you want to explore it further. So pick one that looks interesting and dive into the code!
Wrapping things up
We just spent some time learning the second pattern in the series, where we can use arguments more flexibly by using ref
and unref
intelligently in our composables. The composable will still work whether you happen to have a ref
or just the raw Javascript value. It adapts to how you use it!
We also looked at how the VueUse library implements this pattern in the useTitle
and useCssVar
composables. The useTitle
composable uses the ref
function, and the useCssVar
uses the unref
function so that we could see both variations in action.
In the next lesson, we’ll look at a pattern to improve return values by making them dynamic. We’ll learn how we can return either a single value or an object, depending on what is needed:
// Returns a single value
const isDark = useDark();
// Returns an object of values
const {
counter,
pause,
resume,
} = useInterval(1000, { controls: true });
This pattern can make your composable a lot simpler to use, especially if you only need a single value most of the time.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2023-02-04 [Docker] Remove all containers and images
2023-02-04 [Docker] Build multi stage image
2022-02-04 [RxJS] Using iif
2020-02-04 [NestJS] NestJs Data Validation with ValidationPipe
2016-02-04 [Unit Testing] Based on input value, spyOn function
2016-02-04 [Cycle.js] Generalizing run() function for more types of sources
2016-02-04 [Redux] Extracting Container Components -- Complete