I currently use it for a bunch of things, with automatically detecting user theme preference being my most common and handy use.
With Vue, it's as simple as making the callback modify a reactive variable.
You can also easily make a "composable", which is very similar to your React example.
Based partly on the VueUse library's one:
useMediaQuery.ts
import{onBeforeUnmount}from"vue";exportuseMediaQuery(query:string){// Create the media queryconstmediaQuery=window.matchMedia(query);// Create a ref to hold the state of the match,// initialized with the current value of `matches`.// This is what we will return.constmatches=ref(mediaQuery.matches);// Create a handler to update the value of the ref.consthandler=(event:MediaQueryListEvent)=>{matches.value=event.matches;}// Attach the handler to the media query.if("addEventListener"inmediaQuery)mediaQuery.addEventListener("change",handler);elsemediaQuery.addListener(handler);// Attach a callback that removes the event listener when the component is unmounted.onBeforeUnmount(()=>{if("removeEventListener"inmediaQuery)mediaQuery.removeEventListener("change",handler);elsemediaQuery.removeListener(handler);});returnmatches;}
AwesomeComponent.vue
...<scriptsetup>import{watch,computed}from"vue";import{useMediaQuery}from"@/composables/useMediaQuery";// We can use this ref like any other,// to do cool things like watching it or making computed refs.const{matches}=useMediaQuery("(prefers-color-scheme: dark)");// Watch the ref and change the theme based on the user's preference.watch(matches,(value)=>{// enable/disable dark mode based on `value`.});// We can use this `color` ref in the template, and it will automatically update.constcolor=computed(()=>{returnmatches?"dark":"primary";});</script>
...
You do not really need to allow a callback to be passed in, as you can simply watch the returned ref.
Maybe the timing of reactivity updates would make it necessary, so that the callback gets called immediately when the media query changes, but I think that would be an extreme edge case.
useEventListener is also a common composable.
useEventListener.ts
import{isRef,unref,watch,onMounted,onBeforeUnmount}from"vue";exportfunctionuseEventListener(// the target could be reactive ref which adds flexibilitytarget:Ref<EventTarget|null>|EventTarget,event:string,handler:(e:Event)=>any){// If it is a ref, use a watcher that removes the event listener// from the previous target and attaches it to the new target.letstopWatcher=()=>{};if(isRef(target)){stopWatcher=watch(target,(value,oldValue)=>{oldValue?.removeEventListener(event,handler);value?.addEventListener(event,handler);},{immediate:true},// Run the callback immediately.);}else{// Else use the mounted hookonMounted(()=>{target.addEventListener(event,handler);});}// Create a function that cleans up the event listener.conststop=()=>{stopWatcher();unref(target)?.removeEventListener(event,handler);};// Clean it up when the component is being unmounted.onBeforeUnmount(stop);// Return the function so that the user can remove the event listener if they want to.returnstop;}
Using this, we can rewrite useMediaQuery.
useMediaQuery.ts
import{useEventListener}from"@/composables/useEventListener";exportuseMediaQuery(query:string){// Create the media queryconstmediaQuery=window.matchMedia(query);// Create a ref to hold the state of the match,// initialized with the current value of `matches`.// This is what we will return.constmatches=ref(mediaQuery.matches);// Create a handler to update the value of the ref.consthandler=(event:MediaQueryListEvent)=>{matches.value=event.matches;}// Use the composable to handle the event listener.useEventListener(mediaQuery,"change",handler);returnmatches;}
Note: this will no longer work with browsers that only support addListener and removeListener and not addEventListener and removeEventListener.
We could also make this optionally take a ref to a string instead of just a string, and update the media query when it is changed, like what happens with useEventListener. Or we could simplify useEventListener :).
But for useEventListener, having a reactive target makes sense, as this is useful in many cases, while useMediaQuery doesn't really need that functionality.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
I currently use it for a bunch of things, with automatically detecting user theme preference being my most common and handy use.
With Vue, it's as simple as making the callback modify a reactive variable.
You can also easily make a "composable", which is very similar to your React example.
Based partly on the VueUse library's one:
useMediaQuery.ts
AwesomeComponent.vue
You do not really need to allow a callback to be passed in, as you can simply watch the returned
ref
.Maybe the timing of reactivity updates would make it necessary, so that the callback gets called immediately when the media query changes, but I think that would be an extreme edge case.
useEventListener
is also a common composable.useEventListener.ts
Using this, we can rewrite useMediaQuery.
useMediaQuery.ts
Note: this will no longer work with browsers that only support
addListener
andremoveListener
and notaddEventListener
andremoveEventListener
.We could also make this optionally take a ref to a string instead of just a string, and update the media query when it is changed, like what happens with
useEventListener
. Or we could simplifyuseEventListener
:).But for
useEventListener
, having a reactive target makes sense, as this is useful in many cases, whileuseMediaQuery
doesn't really need that functionality.