From f39691f7a6b88b6b7db89c27d4cb08ef8f48c191 Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Sun, 30 Jul 2023 16:10:55 +0300 Subject: [PATCH] fix(useMediaQuery): only add/remove event listeners on query change (#3236) Co-authored-by: Anthony Fu --- package.json | 2 +- packages/core/useMediaQuery/index.ts | 35 ++++++++++++----------- packages/core/useWindowSize/index.test.ts | 16 +++++++++-- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index f2f9c716a4d..0e51384e13b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build:redirects": "esno scripts/redirects.ts", "build:rollup": "NODE_OPTIONS=\"--max-old-space-size=6144\" rollup -c", "build:types": "tsc --emitDeclarationOnly && nr types:fix", - "clean": "rimraf dist types \"packages/*/dist\"", + "clean": "rimraf --glob dist types \"packages/*/dist\"", "dev": "nr update && nr docs", "docs": "vue-demi-switch 3 && vitepress dev packages --open", "docs:build": "nr update:full && vitepress build packages && nr build:redirects && esno scripts/post-docs.ts", diff --git a/packages/core/useMediaQuery/index.ts b/packages/core/useMediaQuery/index.ts index 72c4461a45b..80293a488a2 100644 --- a/packages/core/useMediaQuery/index.ts +++ b/packages/core/useMediaQuery/index.ts @@ -2,7 +2,7 @@ import { ref, watchEffect } from 'vue-demi' import type { MaybeRefOrGetter } from '@vueuse/shared' -import { toRef, tryOnScopeDispose } from '@vueuse/shared' +import { toValue, tryOnScopeDispose } from '@vueuse/shared' import type { ConfigurableWindow } from '../_configurable' import { defaultWindow } from '../_configurable' import { useSupported } from '../useSupported' @@ -21,39 +21,42 @@ export function useMediaQuery(query: MaybeRefOrGetter, options: Configur let mediaQuery: MediaQueryList | undefined const matches = ref(false) + const handler = (event: MediaQueryListEvent) => { + matches.value = event.matches + } + const cleanup = () => { if (!mediaQuery) return if ('removeEventListener' in mediaQuery) - // eslint-disable-next-line @typescript-eslint/no-use-before-define - mediaQuery.removeEventListener('change', update) + mediaQuery.removeEventListener('change', handler) else // @ts-expect-error deprecated API - // eslint-disable-next-line @typescript-eslint/no-use-before-define - mediaQuery.removeListener(update) + mediaQuery.removeListener(handler) } - const update = () => { + const stopWatch = watchEffect(() => { if (!isSupported.value) return cleanup() - mediaQuery = window!.matchMedia(toRef(query).value) - matches.value = !!mediaQuery?.matches - - if (!mediaQuery) - return + mediaQuery = window!.matchMedia(toValue(query)) if ('addEventListener' in mediaQuery) - mediaQuery.addEventListener('change', update) + mediaQuery.addEventListener('change', handler) else // @ts-expect-error deprecated API - mediaQuery.addListener(update) - } - watchEffect(update) + mediaQuery.addListener(handler) - tryOnScopeDispose(() => cleanup()) + matches.value = mediaQuery.matches + }) + + tryOnScopeDispose(() => { + stopWatch() + cleanup() + mediaQuery = undefined + }) return matches } diff --git a/packages/core/useWindowSize/index.test.ts b/packages/core/useWindowSize/index.test.ts index 8f7ca6f39ef..e61946c995d 100644 --- a/packages/core/useWindowSize/index.test.ts +++ b/packages/core/useWindowSize/index.test.ts @@ -4,10 +4,20 @@ import { useWindowSize } from '.' describe('useWindowSize', () => { const addEventListenerSpy = vi.spyOn(window, 'addEventListener') - const matchMediaSpy = vi.spyOn(window, 'matchMedia') + const matchMediaSpy = vi.spyOn(window, 'matchMedia').mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })) + beforeEach(() => { - addEventListenerSpy.mockReset() - matchMediaSpy.mockReset() + addEventListenerSpy.mockClear() + matchMediaSpy.mockClear() }) afterAll(() => {