From 9c1957fd55b7b354a8b34edfc8341fe493c198d4 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Fri, 31 Mar 2023 17:13:13 +0200 Subject: [PATCH 1/6] Update event listener hooks - Remove useEventListener function overloads - Remove useDocument hook - Remove useWindow hook - Add useDocument hook - Add useWindow hook - Add RefObjectOption to useEventListener for ref support #101 #102 --- src/hooks/useDocument/useDocument.mdx | 28 + src/hooks/useDocument/useDocument.test.tsx | 10 + src/hooks/useDocument/useDocument.ts | 9 + .../useDocumentEventListener.stories.mdx | 26 - .../useDocumentEventListener.stories.tsx | 38 - .../useDocumentEventListener.test.tsx | 18 - .../useDocumentEventListener.ts | 12 - .../useEventListener.stories.tsx | 7 +- .../useEventListener.test.tsx | 56 +- .../useEventListener/useEventListener.ts | 1689 +---------------- src/hooks/useHasFocus/useHasFocus.ts | 9 +- .../useMediaQuery/useMediaQuery.component.tsx | 2 +- .../useMediaQuery.playwright.tsx | 14 +- src/hooks/useMediaQuery/useMediaQuery.ts | 4 +- src/hooks/useRefValue/useRefValue.ts | 8 + src/hooks/useWindow/useWindow.mdx | 28 + src/hooks/useWindow/useWindow.test.ts | 10 + src/hooks/useWindow/useWindow.ts | 9 + .../useWindowEventListener.stories.mdx | 30 - .../useWindowEventListener.stories.tsx | 33 - .../useWindowEventListener.test.tsx | 20 - .../useWindowEventListener.ts | 12 - src/index.ts | 6 +- .../getRefObjectOption/getRefObjectOption.mdx | 53 + .../getRefObjectOption.test.ts | 21 + .../getRefObjectOption/getRefObjectOption.ts | 23 + 26 files changed, 286 insertions(+), 1889 deletions(-) create mode 100644 src/hooks/useDocument/useDocument.mdx create mode 100644 src/hooks/useDocument/useDocument.test.tsx create mode 100644 src/hooks/useDocument/useDocument.ts delete mode 100644 src/hooks/useDocumentEventListener/useDocumentEventListener.stories.mdx delete mode 100644 src/hooks/useDocumentEventListener/useDocumentEventListener.stories.tsx delete mode 100644 src/hooks/useDocumentEventListener/useDocumentEventListener.test.tsx delete mode 100644 src/hooks/useDocumentEventListener/useDocumentEventListener.ts create mode 100644 src/hooks/useRefValue/useRefValue.ts create mode 100644 src/hooks/useWindow/useWindow.mdx create mode 100644 src/hooks/useWindow/useWindow.test.ts create mode 100644 src/hooks/useWindow/useWindow.ts delete mode 100644 src/hooks/useWindowEventListener/useWindowEventListener.stories.mdx delete mode 100644 src/hooks/useWindowEventListener/useWindowEventListener.stories.tsx delete mode 100644 src/hooks/useWindowEventListener/useWindowEventListener.test.tsx delete mode 100644 src/hooks/useWindowEventListener/useWindowEventListener.ts create mode 100644 src/utils/getRefObjectOption/getRefObjectOption.mdx create mode 100644 src/utils/getRefObjectOption/getRefObjectOption.test.ts create mode 100644 src/utils/getRefObjectOption/getRefObjectOption.ts diff --git a/src/hooks/useDocument/useDocument.mdx b/src/hooks/useDocument/useDocument.mdx new file mode 100644 index 0000000..dda5251 --- /dev/null +++ b/src/hooks/useDocument/useDocument.mdx @@ -0,0 +1,28 @@ +import { Meta } from '@storybook/blocks'; + + + +# useDocument + +This hook returns the Document instance when it's availbable in the environment. If the Document is +not available, it will return `undefined`. + +## Reference + +```ts +function useDocument(): Document | undefined; +``` + +## Usage + +```tsx +function DemoComponent(): ReactNode { + const document = useDocument(ref, ':focus-within'); + + useEventListener(document, 'focusin', (event) => { + // do something + }); + + return null; +} +``` diff --git a/src/hooks/useDocument/useDocument.test.tsx b/src/hooks/useDocument/useDocument.test.tsx new file mode 100644 index 0000000..c28e9c6 --- /dev/null +++ b/src/hooks/useDocument/useDocument.test.tsx @@ -0,0 +1,10 @@ +import { renderHook } from '@testing-library/react'; +import { useDocument } from './useDocument.js'; + +describe('useEventListener', () => { + it('Should return the document', () => { + const result = renderHook(useDocument); + + expect(result.result.current).toBeInstanceOf(Document); + }); +}); diff --git a/src/hooks/useDocument/useDocument.ts b/src/hooks/useDocument/useDocument.ts new file mode 100644 index 0000000..ea3a617 --- /dev/null +++ b/src/hooks/useDocument/useDocument.ts @@ -0,0 +1,9 @@ +/** + * Returns undefined when in a server environment, otherwise returns document. + * + * Note: be careful with this hook as it returns a different value on on server + * and client. + */ +export function useDocument(): Document | undefined { + return typeof document === 'undefined' ? undefined : document; +} diff --git a/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.mdx b/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.mdx deleted file mode 100644 index ddfc3e8..0000000 --- a/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.mdx +++ /dev/null @@ -1,26 +0,0 @@ -import { Meta } from '@storybook/blocks'; - - - -# useDocumentEventListener - -This hook allows you to add a document event listener and remove it when the component unmounts. - -## Reference - -```ts -export function useDocumentEventListener( - event: T, - callback: (event: DocumentEventMap[T]) => void, -): void; -``` - -## Usage - -```tsx -const [keydown, setKeydown] = useState>([]); - -useDocumentEventListener('keydown', (event) => { - setKeydown((previous) => [...previous, event.key]); -}); -``` diff --git a/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.tsx b/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.tsx deleted file mode 100644 index a85652d..0000000 --- a/src/hooks/useDocumentEventListener/useDocumentEventListener.stories.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable react/jsx-no-literals */ -import type { StoryObj } from '@storybook/react'; -import { type ReactElement, useState } from 'react'; -import { useDocumentEventListener } from './useDocumentEventListener.js'; - -export default { - title: 'hooks/useDocumentEventListener', -}; - -function DemoComponent(): ReactElement { - const [keydown, setKeydown] = useState>([]); - - useDocumentEventListener('keydown', (event) => { - setKeydown((previous) => [...previous, event.key]); - }); - - return ( -
-
-

Instructions!

-

Press a key to trigger the document keydown event.

-
- -
    - {keydown.map((key, index) => ( - // eslint-disable-next-line react/no-array-index-key -
  • {key}
  • - ))} -
-
- ); -} - -export const Demo: StoryObj = { - render() { - return ; - }, -}; diff --git a/src/hooks/useDocumentEventListener/useDocumentEventListener.test.tsx b/src/hooks/useDocumentEventListener/useDocumentEventListener.test.tsx deleted file mode 100644 index bc18627..0000000 --- a/src/hooks/useDocumentEventListener/useDocumentEventListener.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { useDocumentEventListener } from './useDocumentEventListener.js'; - -describe('useDocumentEventListener', () => { - it('should not crash', () => { - renderHook( - () => { - useDocumentEventListener('focusin', () => { - // eslint-disable-next-line no-console - console.log(document.activeElement); - }); - }, - { - initialProps: undefined, - }, - ); - }); -}); diff --git a/src/hooks/useDocumentEventListener/useDocumentEventListener.ts b/src/hooks/useDocumentEventListener/useDocumentEventListener.ts deleted file mode 100644 index e7017b1..0000000 --- a/src/hooks/useDocumentEventListener/useDocumentEventListener.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useEventListener } from '../useEventListener/useEventListener.js'; - -/** - * SSR-safe hook that adds an event listener to the document. - */ -export function useDocumentEventListener( - type: K, - listener: (this: Document, event: DocumentEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void { - useEventListener(typeof document === 'undefined' ? undefined : document, type, listener, options); -} diff --git a/src/hooks/useEventListener/useEventListener.stories.tsx b/src/hooks/useEventListener/useEventListener.stories.tsx index 186565e..c31b7ef 100644 --- a/src/hooks/useEventListener/useEventListener.stories.tsx +++ b/src/hooks/useEventListener/useEventListener.stories.tsx @@ -1,6 +1,7 @@ /* eslint-disable react/jsx-no-literals */ import type { StoryObj } from '@storybook/react'; -import { type ReactElement, useState } from 'react'; +import { useState, type ReactElement } from 'react'; +import { useDocument } from '../useDocument/useDocument.js'; import { useEventListener } from './useEventListener.js'; export default { @@ -10,7 +11,9 @@ export default { function DemoComponent(): ReactElement { const [text, setText] = useState>([]); - useEventListener(typeof document === 'undefined' ? undefined : document, 'focusin', (event) => { + const document = useDocument(); + + useEventListener(document, 'focusin', (event) => { // eslint-disable-next-line no-console setText((previous) => [ ...previous, diff --git a/src/hooks/useEventListener/useEventListener.test.tsx b/src/hooks/useEventListener/useEventListener.test.tsx index 725190d..8d210ca 100644 --- a/src/hooks/useEventListener/useEventListener.test.tsx +++ b/src/hooks/useEventListener/useEventListener.test.tsx @@ -1,18 +1,48 @@ -import { renderHook } from '@testing-library/react'; +/* eslint-disable react/no-multi-comp */ +import { jest } from '@jest/globals'; +import { render } from '@testing-library/react'; +import { createRef, useEffect, useState, type ReactElement } from 'react'; import { useEventListener } from './useEventListener.js'; describe('useEventListener', () => { - it('should not crash', () => { - renderHook( - () => { - useEventListener(typeof document === 'undefined' ? undefined : document, 'focusin', () => { - // eslint-disable-next-line no-console - console.log(document.activeElement); - }); - }, - { - initialProps: undefined, - }, - ); + it('Should listen to event attached to element from RefObject', () => { + const spy = jest.fn(); + const ref = createRef(); + + function Test(): ReactElement { + useEventListener(ref, 'click', spy); + + return
; + } + + render(); + + ref.current?.click(); + + expect(spy).toBeCalledTimes(1); + }); + + it('Should listen to event attached to element from state', async () => { + const spy = jest.fn(); + let exposedRef: HTMLDivElement | null = null; + + function Test(): ReactElement { + const [ref, setRef] = useState(null); + + useEffect(() => { + exposedRef = ref; + }, [ref]); + + useEventListener(ref, 'click', spy); + + return
; + } + + render(); + + // @ts-expect-error typescript doesn't infer type for exposedRef correctly + exposedRef?.click(); + + expect(spy).toBeCalledTimes(1); }); }); diff --git a/src/hooks/useEventListener/useEventListener.ts b/src/hooks/useEventListener/useEventListener.ts index ef52e39..afe4ce2 100644 --- a/src/hooks/useEventListener/useEventListener.ts +++ b/src/hooks/useEventListener/useEventListener.ts @@ -1,1679 +1,30 @@ -/* eslint-disable @typescript-eslint/unified-signatures */ +/* eslint-disable @typescript-eslint/unified-signatures, @typescript-eslint/no-explicit-any */ import { useEffect } from 'react'; +import { + getRefObjectOptionValue, + type RefObjectOption, +} from '../../utils/getRefObjectOption/getRefObjectOption.js'; +import { useRefValue } from '../useRefValue/useRefValue.js'; -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: AbstractWorker, event: AbstractWorkerEventMap[K]) => void, +export function useEventListener( + targetOption: RefObjectOption | null | undefined, + type: string, + listener: EventListener, options?: boolean | AddEventListenerOptions, -): void; - -// @ts-expect-error function overload is correct but typescript doesn't like it -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Animation, event: AnimationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends AudioBufferSourceNode, - K extends keyof AudioScheduledSourceNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: AudioBufferSourceNode, event: AudioScheduledSourceNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: AudioContext, event: BaseAudioContextEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends AudioScheduledSourceNode, - K extends keyof AudioScheduledSourceNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: AudioScheduledSourceNode, event: AudioScheduledSourceNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends AudioWorkletNode, - K extends keyof AudioWorkletNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: AudioWorkletNode, event: AudioWorkletNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends BaseAudioContext, - K extends keyof BaseAudioContextEventMap, ->( - target: T | undefined, - type: K, - listener: (this: BaseAudioContext, event: BaseAudioContextEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends BroadcastChannel, - K extends keyof BroadcastChannelEventMap, ->( - target: T | undefined, - type: K, - listener: (this: BroadcastChannel, event: BroadcastChannelEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: CSSAnimation, event: AnimationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: CSSTransition, event: AnimationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends CanvasCaptureMediaStreamTrack, - K extends keyof MediaStreamTrackEventMap, ->( - target: T | undefined, - type: K, - listener: (this: CanvasCaptureMediaStreamTrack, event: MediaStreamTrackEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends ConstantSourceNode, - K extends keyof AudioScheduledSourceNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: ConstantSourceNode, event: AudioScheduledSourceNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Document, event: DocumentEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends DocumentAndElementEventHandlers, - K extends keyof DocumentAndElementEventHandlersEventMap, ->( - target: T | undefined, - type: K, - listener: ( - this: DocumentAndElementEventHandlers, - event: DocumentAndElementEventHandlersEventMap[K], - ) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Element, event: ElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: EventSource, event: EventSourceEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: FileReader, event: FileReaderEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: FontFaceSet, event: FontFaceSetEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends GlobalEventHandlers, - K extends keyof GlobalEventHandlersEventMap, ->( - target: T | undefined, - type: K, - listener: (this: GlobalEventHandlers, event: GlobalEventHandlersEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLAnchorElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLAreaElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLAudioElement, - K extends keyof HTMLMediaElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLAudioElement, event: HTMLMediaElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLBRElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLBaseElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLBodyElement, - K extends keyof HTMLBodyElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLBodyElement, event: HTMLBodyElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLButtonElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLCanvasElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLDListElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLDataElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLDataListElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLDataListElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLDetailsElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLDialogElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLDivElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLEmbedElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLFieldSetElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLFieldSetElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLFormElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLHRElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLHeadElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLHeadingElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLHtmlElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLIFrameElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLImageElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLInputElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLLIElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLLabelElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLLegendElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLLinkElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLMapElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLMediaElement, - K extends keyof HTMLMediaElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLMediaElement, event: HTMLMediaElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLMenuElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLMetaElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLMeterElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLModElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLOListElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLObjectElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLOptGroupElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLOptGroupElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLOptionElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLOutputElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLParagraphElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLParagraphElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLPictureElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLPreElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLProgressElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLProgressElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLQuoteElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLScriptElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLSelectElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLSlotElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLSourceElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLSpanElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLStyleElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTableCaptionElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTableCaptionElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTableCellElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTableCellElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTableColElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTableColElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLTableElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTableRowElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTableRowElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTableSectionElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTableSectionElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTemplateElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTemplateElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLTextAreaElement, - K extends keyof HTMLElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLTextAreaElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLTimeElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLTitleElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLTrackElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLUListElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: HTMLUnknownElement, event: HTMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends HTMLVideoElement, - K extends keyof HTMLVideoElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: HTMLVideoElement, event: HTMLVideoElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: IDBDatabase, event: IDBDatabaseEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends IDBOpenDBRequest, - K extends keyof IDBOpenDBRequestEventMap, ->( - target: T | undefined, - type: K, - listener: (this: IDBOpenDBRequest, event: IDBOpenDBRequestEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: IDBRequest, event: IDBRequestEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: IDBTransaction, event: IDBTransactionEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MathMLElement, event: MathMLElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MediaDevices, event: MediaDevicesEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends MediaKeySession, - K extends keyof MediaKeySessionEventMap, ->( - target: T | undefined, - type: K, - listener: (this: MediaKeySession, event: MediaKeySessionEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MediaQueryList, event: MediaQueryListEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MediaRecorder, event: MediaRecorderEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MediaSource, event: MediaSourceEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MediaStream, event: MediaStreamEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends MediaStreamTrack, - K extends keyof MediaStreamTrackEventMap, ->( - target: T | undefined, - type: K, - listener: (this: MediaStreamTrack, event: MediaStreamTrackEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: MessagePort, event: MessagePortEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Notification, event: NotificationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends OfflineAudioContext, - K extends keyof OfflineAudioContextEventMap, ->( - target: T | undefined, - type: K, - listener: (this: OfflineAudioContext, event: OfflineAudioContextEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends OffscreenCanvas, - K extends keyof OffscreenCanvasEventMap, ->( - target: T | undefined, - type: K, - listener: (this: OffscreenCanvas, event: OffscreenCanvasEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends OscillatorNode, - K extends keyof AudioScheduledSourceNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: OscillatorNode, event: AudioScheduledSourceNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: PaymentRequest, event: PaymentRequestEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Performance, event: PerformanceEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends PermissionStatus, - K extends keyof PermissionStatusEventMap, ->( - target: T | undefined, - type: K, - listener: (this: PermissionStatus, event: PermissionStatusEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends PictureInPictureWindow, - K extends keyof PictureInPictureWindowEventMap, ->( - target: T | undefined, - type: K, - listener: (this: PictureInPictureWindow, event: PictureInPictureWindowEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: RTCDTMFSender, event: RTCDTMFSenderEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: RTCDataChannel, event: RTCDataChannelEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends RTCDtlsTransport, - K extends keyof RTCDtlsTransportEventMap, ->( - target: T | undefined, - type: K, - listener: (this: RTCDtlsTransport, event: RTCDtlsTransportEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends RTCIceTransport, - K extends keyof RTCIceTransportEventMap, ->( - target: T | undefined, - type: K, - listener: (this: RTCIceTransport, event: RTCIceTransportEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends RTCPeerConnection, - K extends keyof RTCPeerConnectionEventMap, ->( - target: T | undefined, - type: K, - listener: (this: RTCPeerConnection, event: RTCPeerConnectionEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends RTCSctpTransport, - K extends keyof RTCSctpTransportEventMap, ->( - target: T | undefined, - type: K, - listener: (this: RTCSctpTransport, event: RTCSctpTransportEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: RemotePlayback, event: RemotePlaybackEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGAElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGAnimateElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGAnimateMotionElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGAnimateMotionElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGAnimateTransformElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGAnimateTransformElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGAnimationElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGCircleElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGClipPathElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGComponentTransferFunctionElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGComponentTransferFunctionElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGDefsElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGDescElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGEllipseElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEBlendElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEColorMatrixElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEColorMatrixElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEComponentTransferElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEComponentTransferElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFECompositeElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFECompositeElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEConvolveMatrixElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEConvolveMatrixElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEDiffuseLightingElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEDiffuseLightingElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEDisplacementMapElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEDisplacementMapElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEDistantLightElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEDistantLightElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEDropShadowElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEDropShadowElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEFloodElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEFuncAElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEFuncBElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEFuncGElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEFuncRElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEGaussianBlurElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEGaussianBlurElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEImageElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEMergeElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEMergeNodeElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEMergeNodeElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEMorphologyElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEMorphologyElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFEOffsetElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFEPointLightElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFEPointLightElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFESpecularLightingElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFESpecularLightingElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFESpotLightElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFESpotLightElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFETileElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGFETurbulenceElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGFETurbulenceElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGFilterElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGForeignObjectElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGForeignObjectElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGGElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGGeometryElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGGradientElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGGraphicsElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGImageElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGLineElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGLinearGradientElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGLinearGradientElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGMPathElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGMarkerElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGMaskElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGMetadataElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGPathElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGPatternElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGPolygonElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGPolylineElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGRadialGradientElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGRadialGradientElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGRectElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGSVGElement, event: SVGSVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGScriptElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGSetElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGStopElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGStyleElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGSwitchElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGSymbolElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGTSpanElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGTextContentElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGTextContentElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGTextElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGTextPathElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SVGTextPositioningElement, - K extends keyof SVGElementEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SVGTextPositioningElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGTitleElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGUseElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SVGViewElement, event: SVGElementEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends ScreenOrientation, - K extends keyof ScreenOrientationEventMap, ->( - target: T | undefined, - type: K, - listener: (this: ScreenOrientation, event: ScreenOrientationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends ScriptProcessorNode, - K extends keyof ScriptProcessorNodeEventMap, ->( - target: T | undefined, - type: K, - listener: (this: ScriptProcessorNode, event: ScriptProcessorNodeEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: ServiceWorker, event: ServiceWorkerEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends ServiceWorkerContainer, - K extends keyof ServiceWorkerContainerEventMap, ->( - target: T | undefined, - type: K, - listener: (this: ServiceWorkerContainer, event: ServiceWorkerContainerEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends ServiceWorkerRegistration, - K extends keyof ServiceWorkerRegistrationEventMap, ->( - target: T | undefined, - type: K, - listener: (this: ServiceWorkerRegistration, event: ServiceWorkerRegistrationEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: ShadowRoot, event: ShadowRootEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SharedWorker, event: AbstractWorkerEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: SourceBuffer, event: SourceBufferEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SourceBufferList, - K extends keyof SourceBufferListEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SourceBufferList, event: SourceBufferListEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SpeechSynthesis, - K extends keyof SpeechSynthesisEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SpeechSynthesis, event: SpeechSynthesisEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends SpeechSynthesisUtterance, - K extends keyof SpeechSynthesisUtteranceEventMap, ->( - target: T | undefined, - type: K, - listener: (this: SpeechSynthesisUtterance, event: SpeechSynthesisUtteranceEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: TextTrack, event: TextTrackEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: TextTrackCue, event: TextTrackCueEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: TextTrackList, event: TextTrackListEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: VTTCue, event: TextTrackCueEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: VisualViewport, event: VisualViewportEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: WebSocket, event: WebSocketEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Window, event: WindowEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener< - T extends WindowEventHandlers, - K extends keyof WindowEventHandlersEventMap, ->( - target: T | undefined, - type: K, - listener: (this: WindowEventHandlers, event: WindowEventHandlersEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Worker, event: WorkerEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; - -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: XMLDocument, event: DocumentEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; +): void { + const listenerRef = useRefValue(listener); -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: XMLHttpRequest, event: XMLHttpRequestEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; + useEffect(() => { + const target = getRefObjectOptionValue(targetOption); -export function useEventListener< - T extends XMLHttpRequestEventTarget, - K extends keyof XMLHttpRequestEventTargetEventMap, ->( - target: T | undefined, - type: K, - listener: (this: XMLHttpRequestEventTarget, event: XMLHttpRequestEventTargetEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; + function callback(this: unknown, event: Event): void { + listenerRef.current?.(event); + } -export function useEventListener< - T extends XMLHttpRequestUpload, - K extends keyof XMLHttpRequestEventTargetEventMap, ->( - target: T | undefined, - type: K, - listener: (this: XMLHttpRequestUpload, event: XMLHttpRequestEventTargetEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void; + target?.addEventListener(type, callback, options); -export function useEventListener( - target: T | undefined, - type: K, - listener: (this: Window, event: WindowEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void { - useEffect(() => { - target?.addEventListener(type, listener, options); return () => { - target?.removeEventListener(type, listener, options); + target?.removeEventListener(type, callback, options); }; - }, [target, type, listener, options]); + }, [listenerRef, options, targetOption, type]); } diff --git a/src/hooks/useHasFocus/useHasFocus.ts b/src/hooks/useHasFocus/useHasFocus.ts index 3f00675..cfca9b1 100644 --- a/src/hooks/useHasFocus/useHasFocus.ts +++ b/src/hooks/useHasFocus/useHasFocus.ts @@ -1,5 +1,6 @@ import { useCallback, useState, type RefObject } from 'react'; -import { useDocumentEventListener } from '../useDocumentEventListener/useDocumentEventListener.js'; +import { useDocument } from '../useDocument/useDocument.js'; +import { useEventListener } from '../useEventListener/useEventListener.js'; export type FocusPseudoSelector = ':focus' | ':focus-visible' | ':focus-within'; @@ -21,8 +22,10 @@ export function useHasFocus( setState(matches()); }, [matches, setState]); - useDocumentEventListener('focusin', onUpdate); - useDocumentEventListener('focusout', onUpdate); + const document = useDocument(); + + useEventListener(document, 'focusin', onUpdate); + useEventListener(document, 'focusout', onUpdate); return state; } diff --git a/src/hooks/useMediaQuery/useMediaQuery.component.tsx b/src/hooks/useMediaQuery/useMediaQuery.component.tsx index e1e7354..bbdc87f 100644 --- a/src/hooks/useMediaQuery/useMediaQuery.component.tsx +++ b/src/hooks/useMediaQuery/useMediaQuery.component.tsx @@ -1,7 +1,7 @@ import type { ReactElement } from 'react'; import { useMediaQuery } from './useMediaQuery.js'; -export function Component(): ReactElement { +export function UseMediaQueryComponent(): ReactElement { // @ts-expect-error variable is defined in index.html const isMinWidth480px = useMediaQuery('--min-width-480'); diff --git a/src/hooks/useMediaQuery/useMediaQuery.playwright.tsx b/src/hooks/useMediaQuery/useMediaQuery.playwright.tsx index 7b48ed9..ac7038b 100644 --- a/src/hooks/useMediaQuery/useMediaQuery.playwright.tsx +++ b/src/hooks/useMediaQuery/useMediaQuery.playwright.tsx @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/experimental-ct-react'; -import { Component } from './useMediaQuery.component.js'; +import { UseMediaQueryComponent } from './useMediaQuery.component.js'; test('Should detect initial screen size', async ({ page, mount }) => { await page.setViewportSize({ @@ -7,13 +7,13 @@ test('Should detect initial screen size', async ({ page, mount }) => { height: 480, }); - const component = await mount(, {}); + const component = await mount(); await expect(component).toContainText('large'); }); test('Should detect screen size changes', async ({ page, mount }) => { - await mount(, {}); + await mount(); await page.setViewportSize({ width: 250, @@ -21,16 +21,12 @@ test('Should detect screen size changes', async ({ page, mount }) => { }); // Wait for the resize to be handled by the hook - await page.getByText('small').waitFor({ - timeout: 100, - }); + await page.getByText('small').waitFor(); await page.setViewportSize({ width: 480, height: 480, }); - await page.getByText('large').waitFor({ - timeout: 100, - }); + await page.getByText('large').waitFor(); }); diff --git a/src/hooks/useMediaQuery/useMediaQuery.ts b/src/hooks/useMediaQuery/useMediaQuery.ts index 710cb90..c408175 100644 --- a/src/hooks/useMediaQuery/useMediaQuery.ts +++ b/src/hooks/useMediaQuery/useMediaQuery.ts @@ -50,7 +50,9 @@ export function useMediaQuery(mediaQueryVariableName: MediaQueries, defaultValue }, [defaultValue, mediaQueryList?.matches, mediaQueryVariableName]); useEventListener(mediaQueryList, 'change', (event) => { - setMatches(event.matches); + if (event instanceof MediaQueryListEvent) { + setMatches(event.matches); + } }); return matches ?? defaultValue; diff --git a/src/hooks/useRefValue/useRefValue.ts b/src/hooks/useRefValue/useRefValue.ts new file mode 100644 index 0000000..54ecf7b --- /dev/null +++ b/src/hooks/useRefValue/useRefValue.ts @@ -0,0 +1,8 @@ +import { type RefObject, useRef } from 'react'; + +export function useRefValue(value: T): RefObject { + const ref = useRef(value); + ref.current = value; + + return ref; +} diff --git a/src/hooks/useWindow/useWindow.mdx b/src/hooks/useWindow/useWindow.mdx new file mode 100644 index 0000000..21a36c7 --- /dev/null +++ b/src/hooks/useWindow/useWindow.mdx @@ -0,0 +1,28 @@ +import { Meta } from '@storybook/blocks'; + + + +# useWindow + +This hook returns the Window instance when it's availbable in the environment. If the Window is not +available, it will return `undefined`. + +## Reference + +```ts +function useWindow(): Window | undefined; +``` + +## Usage + +```tsx +function DemoComponent(): ReactNode { + const window = useWindow(ref, ':focus-within'); + + useEventListener(window, 'keydown', (event) => { + // do something + }); + + return null; +} +``` diff --git a/src/hooks/useWindow/useWindow.test.ts b/src/hooks/useWindow/useWindow.test.ts new file mode 100644 index 0000000..3c358c4 --- /dev/null +++ b/src/hooks/useWindow/useWindow.test.ts @@ -0,0 +1,10 @@ +import { renderHook } from '@testing-library/react'; +import { useWindow } from './useWindow.js'; + +describe('useWindow', () => { + it('Should return the window', () => { + const result = renderHook(useWindow); + + expect(result.result.current).toBe(window); + }); +}); diff --git a/src/hooks/useWindow/useWindow.ts b/src/hooks/useWindow/useWindow.ts new file mode 100644 index 0000000..4f3ce01 --- /dev/null +++ b/src/hooks/useWindow/useWindow.ts @@ -0,0 +1,9 @@ +/** + * Returns undefined when in a server environment, otherwise returns window. + * + * Note: be careful when using this hook, it returns a different value on on server + * and client. + */ +export function useWindow(): (Window & typeof globalThis) | undefined { + return typeof window === 'undefined' ? undefined : window; +} diff --git a/src/hooks/useWindowEventListener/useWindowEventListener.stories.mdx b/src/hooks/useWindowEventListener/useWindowEventListener.stories.mdx deleted file mode 100644 index 438bf8b..0000000 --- a/src/hooks/useWindowEventListener/useWindowEventListener.stories.mdx +++ /dev/null @@ -1,30 +0,0 @@ -import { Meta } from '@storybook/blocks'; - - - -# useWindowEventListener - -This hook allows you to add a window event listener and remove it when the component unmounts. - -## Reference - -```ts -function useWindowEventListener( - event: T, - callback: (event: WindowEventMap[T]) => void, -): void; -``` - -## Usage - -```tsx -function DemoComponent(): ReactElement { - const [size, setSize] = useState([0, 0]); - - useWindowEventListener('resize', () => { - setSize([window.innerWidth, window.innerHeight]); - }); - - return
{size.join('x')}
; -} -``` diff --git a/src/hooks/useWindowEventListener/useWindowEventListener.stories.tsx b/src/hooks/useWindowEventListener/useWindowEventListener.stories.tsx deleted file mode 100644 index 3e9d6be..0000000 --- a/src/hooks/useWindowEventListener/useWindowEventListener.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable react/jsx-no-literals */ -import type { StoryObj } from '@storybook/react'; -import { type ReactElement, useState } from 'react'; -import { useWindowEventListener } from './useWindowEventListener.js'; - -export default { - title: 'hooks/useWindowEventListener', -}; - -function DemoComponent(): ReactElement { - const [size, setSize] = useState([0, 0]); - - useWindowEventListener('resize', () => { - setSize([window.innerWidth, window.innerHeight]); - }); - - return ( -
-
-

Instructions!

-

Resize the viewport to listen to the resize event on the window.

-
- -
Viewport size: {size.join('x')}
-
- ); -} - -export const Demo: StoryObj = { - render() { - return ; - }, -}; diff --git a/src/hooks/useWindowEventListener/useWindowEventListener.test.tsx b/src/hooks/useWindowEventListener/useWindowEventListener.test.tsx deleted file mode 100644 index e7d7551..0000000 --- a/src/hooks/useWindowEventListener/useWindowEventListener.test.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { jest } from '@jest/globals'; -import { renderHook } from '@testing-library/react'; -import { useWindowEventListener } from './useWindowEventListener.js'; - -describe('useWindowEventListener', () => { - it('should not crash', async () => { - const spy = jest.fn(); - - renderHook(() => { - useWindowEventListener('resize', () => { - spy(); - - // eslint-disable-next-line no-console - console.log('window resized'); - }); - }); - - expect(spy).not.toHaveBeenCalled(); - }); -}); diff --git a/src/hooks/useWindowEventListener/useWindowEventListener.ts b/src/hooks/useWindowEventListener/useWindowEventListener.ts deleted file mode 100644 index 20b7db6..0000000 --- a/src/hooks/useWindowEventListener/useWindowEventListener.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useEventListener } from '../useEventListener/useEventListener.js'; - -/** - * SSR-safe hook that adds an event listener to the window. - */ -export function useWindowEventListener( - type: K, - listener: (this: Window, event: WindowEventMap[K]) => void, - options?: boolean | AddEventListenerOptions, -): void { - useEventListener(typeof window === 'undefined' ? undefined : window, type, listener, options); -} diff --git a/src/index.ts b/src/index.ts index 4670f9f..7baa2e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ /* PLOP_ADD_EXPORT */ export * from './components/AutoFill/AutoFill.js'; export * from './hocs/ensuredForwardRef/ensuredForwardRef.js'; -export * from './hooks/useDocumentEventListener/useDocumentEventListener.js'; +export * from './hooks/useDocument/useDocument.js'; export * from './hooks/useEventListener/useEventListener.js'; export * from './hooks/useHasFocus/useHasFocus.js'; export * from './hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js'; @@ -12,9 +12,11 @@ export * from './hooks/useRefs/useRefs.types.js'; export * from './hooks/useRefs/utils/assertAndUnwrapRefs/assertAndUnwrapRefs.js'; export * from './hooks/useRefs/utils/unwrapRefs/unwrapRefs.js'; export * from './hooks/useRefs/utils/unwrapRefs/unwrapRefs.types.js'; +export * from './hooks/useRefValue/useRefValue.js'; export * from './hooks/useRegisterRef/useRegisterRef.js'; export * from './hooks/useResizeObserver/useResizeObserver.js'; export * from './hooks/useToggle/useToggle.js'; export * from './hooks/useUnmount/useUnmount.js'; -export * from './hooks/useWindowEventListener/useWindowEventListener.js'; +export * from './hooks/useWindow/useWindow.js'; export * from './utils/arrayRef/arrayRef.js'; +export * from './utils/getRefObjectOption/getRefObjectOption.js'; diff --git a/src/utils/getRefObjectOption/getRefObjectOption.mdx b/src/utils/getRefObjectOption/getRefObjectOption.mdx new file mode 100644 index 0000000..556a65a --- /dev/null +++ b/src/utils/getRefObjectOption/getRefObjectOption.mdx @@ -0,0 +1,53 @@ +import { Meta } from '@storybook/blocks'; + + + +# getRefObjectOption + +This module contains utilities for working with ref objects. + +## `RefObjectOption` + +Type for a value that is optionally wrapped in a RefObject type. This is useful for cases where a +value may be passed as either a plain value or a RefObject containing the value. + +### Example usage + +```ts +type MyType = RefObjectOption; + +const plainValue = 'foo' satisfies MyType; // plain value +const refObjectValue = { current: 'bar' } satisfies MyType; // RefObject containing value +``` + +## `RefObjectOptionValue>` + +Type to get the value of a RefObjectOption. If the input type is a RefObject, it returns the type +T. Otherwise, it returns the input type itself. + +### Example usage + +```ts +type MyType = RefObjectOption; +type MyValueType = RefObjectOptionValue; // string +``` + +## `getRefObjectOptionValue(target: RefObjectOption): T | null` + +This function allows you to get the current value of a ref object, or the value as is. + +- `target` - The target value, which may be a RefObject or a plain value + +### Returns + +The current value of the ref object, or the input value if it is not a ref object. + +### Example usage + +```ts +const myRefObject = { current: 'foo' }; +getRefObjectOptionValue(myRefObject); // 'foo' + +const myValue = 'bar'; +getRefObjectOptionValue(myValue); // 'bar' +``` diff --git a/src/utils/getRefObjectOption/getRefObjectOption.test.ts b/src/utils/getRefObjectOption/getRefObjectOption.test.ts new file mode 100644 index 0000000..97d98a6 --- /dev/null +++ b/src/utils/getRefObjectOption/getRefObjectOption.test.ts @@ -0,0 +1,21 @@ +import { getRefObjectOptionValue } from './getRefObjectOption.js'; + +describe('getRefObjectOption', () => { + it('should return the current value of the ref object if it exists', () => { + const value = { current: 'hello' }; + const result = getRefObjectOptionValue(value); + + expect(result).toEqual('hello'); + }); + + it('should return the original value if it is not a ref object', () => { + const value = 'world'; + const result = getRefObjectOptionValue(value); + + expect(result).toEqual('world'); + }); + + it('should return null if the input is null', () => { + expect(null).toBeNull(); + }); +}); diff --git a/src/utils/getRefObjectOption/getRefObjectOption.ts b/src/utils/getRefObjectOption/getRefObjectOption.ts new file mode 100644 index 0000000..8ba131f --- /dev/null +++ b/src/utils/getRefObjectOption/getRefObjectOption.ts @@ -0,0 +1,23 @@ +import type { RefObject } from 'react'; + +/** + * Type for a value that is optionally wrapped in a RefObject type + */ +export type RefObjectOption = RefObject | T; + +/** + * Type to get the value of a RefObjectOption + */ +export type RefObjectOptionValue> = T extends RefObject + ? U + : T; + +/** + * This function allows you to get the current value of a ref object, or the + * value as is. + */ +export function getRefObjectOptionValue(target: RefObjectOption): T | null { + return target !== null && typeof target === 'object' && 'current' in target + ? target.current + : target; +} From 1a845977982254e95aa03e44a69c2b0b9e895393 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Mon, 3 Apr 2023 11:51:46 +0200 Subject: [PATCH 2/6] Remove invalid parameters in docs --- src/hooks/useDocument/useDocument.mdx | 2 +- src/hooks/useWindow/useWindow.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDocument/useDocument.mdx b/src/hooks/useDocument/useDocument.mdx index dda5251..1bdf986 100644 --- a/src/hooks/useDocument/useDocument.mdx +++ b/src/hooks/useDocument/useDocument.mdx @@ -17,7 +17,7 @@ function useDocument(): Document | undefined; ```tsx function DemoComponent(): ReactNode { - const document = useDocument(ref, ':focus-within'); + const document = useDocument(); useEventListener(document, 'focusin', (event) => { // do something diff --git a/src/hooks/useWindow/useWindow.mdx b/src/hooks/useWindow/useWindow.mdx index 21a36c7..6201dde 100644 --- a/src/hooks/useWindow/useWindow.mdx +++ b/src/hooks/useWindow/useWindow.mdx @@ -17,7 +17,7 @@ function useWindow(): Window | undefined; ```tsx function DemoComponent(): ReactNode { - const window = useWindow(ref, ':focus-within'); + const window = useWindow(); useEventListener(window, 'keydown', (event) => { // do something From 857d4dcb7d32c3a861e045799e9f51bc621cf8d3 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Mon, 3 Apr 2023 12:33:55 +0200 Subject: [PATCH 3/6] Create isRefObject function See: https://github.com/mediamonks/react-hooks/pull/118#discussion_r1155741760 --- src/index.ts | 3 +- src/utils/isRefObject/isRefObject.mdx | 25 +++++++++++++++ src/utils/isRefObject/isRefObject.test.ts | 37 +++++++++++++++++++++++ src/utils/isRefObject/isRefObject.ts | 8 +++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/utils/isRefObject/isRefObject.mdx create mode 100644 src/utils/isRefObject/isRefObject.test.ts create mode 100644 src/utils/isRefObject/isRefObject.ts diff --git a/src/index.ts b/src/index.ts index 7baa2e7..41759db 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,12 +7,12 @@ export * from './hooks/useHasFocus/useHasFocus.js'; export * from './hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js'; export * from './hooks/useMediaQuery/useMediaQuery.js'; export * from './hooks/useMount/useMount.js'; +export * from './hooks/useRefValue/useRefValue.js'; export * from './hooks/useRefs/useRefs.js'; export * from './hooks/useRefs/useRefs.types.js'; export * from './hooks/useRefs/utils/assertAndUnwrapRefs/assertAndUnwrapRefs.js'; export * from './hooks/useRefs/utils/unwrapRefs/unwrapRefs.js'; export * from './hooks/useRefs/utils/unwrapRefs/unwrapRefs.types.js'; -export * from './hooks/useRefValue/useRefValue.js'; export * from './hooks/useRegisterRef/useRegisterRef.js'; export * from './hooks/useResizeObserver/useResizeObserver.js'; export * from './hooks/useToggle/useToggle.js'; @@ -20,3 +20,4 @@ export * from './hooks/useUnmount/useUnmount.js'; export * from './hooks/useWindow/useWindow.js'; export * from './utils/arrayRef/arrayRef.js'; export * from './utils/getRefObjectOption/getRefObjectOption.js'; +export * from './utils/isRefObject/isRefObject.js'; diff --git a/src/utils/isRefObject/isRefObject.mdx b/src/utils/isRefObject/isRefObject.mdx new file mode 100644 index 0000000..219adcb --- /dev/null +++ b/src/utils/isRefObject/isRefObject.mdx @@ -0,0 +1,25 @@ +import { Meta } from '@storybook/blocks'; + + + +# isRefObject + +Checks if the target is a RefObject. + +## Reference + +```ts +function isRefObject(target: unknown): target is RefObject; +``` + +### Returns + +Boolean indicating if the target is a RefObject. + +## Usage + +```ts +isRefObject({ current: 'foo' }); // true +isRefObject('bar'); // false +isRefObject(null); // false +``` diff --git a/src/utils/isRefObject/isRefObject.test.ts b/src/utils/isRefObject/isRefObject.test.ts new file mode 100644 index 0000000..626da46 --- /dev/null +++ b/src/utils/isRefObject/isRefObject.test.ts @@ -0,0 +1,37 @@ +import { isRefObject } from './isRefObject.js'; + +describe('isRefObject', () => { + it('should return true if the value is a ref object with a truthy value', () => { + const value = { current: 'hello' }; + const result = isRefObject(value); + + expect(result).toBe(true); + }); + + it('should return true if the value is a ref object with a falsy value', () => { + const value = { current: false }; + const result = isRefObject(value); + + expect(result).toBe(true); + }); + + it('should return true if the value is a ref object with `null` value', () => { + const value = { current: null }; + const result = isRefObject(value); + + expect(result).toBe(true); + }); + + it('should return false if value is not a ref object', () => { + const value = 'world'; + const result = isRefObject(value); + + expect(result).toBe(false); + }); + + it('should return false if the input is null', () => { + const result = isRefObject(null); + + expect(result).toBe(false); + }); +}); diff --git a/src/utils/isRefObject/isRefObject.ts b/src/utils/isRefObject/isRefObject.ts new file mode 100644 index 0000000..cab50bb --- /dev/null +++ b/src/utils/isRefObject/isRefObject.ts @@ -0,0 +1,8 @@ +import { type RefObject } from 'react'; + +/** + * Type guard to check if a value is a RefObject + */ +export function isRefObject(target: unknown): target is RefObject { + return target !== null && typeof target === 'object' && 'current' in target; +} From 91b56cec5a89b14c75774f3b8f6b29aa145ddb84 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Mon, 3 Apr 2023 18:01:11 +0200 Subject: [PATCH 4/6] Rename getRefObjectOption to unref --- .../useEventListener/useEventListener.ts | 11 ++++----- src/index.ts | 2 +- .../getRefObjectOption/getRefObjectOption.ts | 23 ------------------ src/utils/isRefObject/isRefObject.ts | 2 +- .../unref.mdx} | 24 +++++++++---------- .../unref.test.ts} | 12 ++++++---- src/utils/unref/unref.ts | 19 +++++++++++++++ 7 files changed, 44 insertions(+), 49 deletions(-) delete mode 100644 src/utils/getRefObjectOption/getRefObjectOption.ts rename src/utils/{getRefObjectOption/getRefObjectOption.mdx => unref/unref.mdx} (58%) rename src/utils/{getRefObjectOption/getRefObjectOption.test.ts => unref/unref.test.ts} (61%) create mode 100644 src/utils/unref/unref.ts diff --git a/src/hooks/useEventListener/useEventListener.ts b/src/hooks/useEventListener/useEventListener.ts index afe4ce2..612796e 100644 --- a/src/hooks/useEventListener/useEventListener.ts +++ b/src/hooks/useEventListener/useEventListener.ts @@ -1,13 +1,10 @@ /* eslint-disable @typescript-eslint/unified-signatures, @typescript-eslint/no-explicit-any */ -import { useEffect } from 'react'; -import { - getRefObjectOptionValue, - type RefObjectOption, -} from '../../utils/getRefObjectOption/getRefObjectOption.js'; +import { useEffect, type RefObject } from 'react'; +import { unref } from '../../utils/unref/unref.js'; import { useRefValue } from '../useRefValue/useRefValue.js'; export function useEventListener( - targetOption: RefObjectOption | null | undefined, + targetOption: RefObject | T | null | undefined, type: string, listener: EventListener, options?: boolean | AddEventListenerOptions, @@ -15,7 +12,7 @@ export function useEventListener( const listenerRef = useRefValue(listener); useEffect(() => { - const target = getRefObjectOptionValue(targetOption); + const target = unref(targetOption); function callback(this: unknown, event: Event): void { listenerRef.current?.(event); diff --git a/src/index.ts b/src/index.ts index 41759db..b7db7ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,5 +19,5 @@ export * from './hooks/useToggle/useToggle.js'; export * from './hooks/useUnmount/useUnmount.js'; export * from './hooks/useWindow/useWindow.js'; export * from './utils/arrayRef/arrayRef.js'; -export * from './utils/getRefObjectOption/getRefObjectOption.js'; export * from './utils/isRefObject/isRefObject.js'; +export * from './utils/unref/unref.js'; diff --git a/src/utils/getRefObjectOption/getRefObjectOption.ts b/src/utils/getRefObjectOption/getRefObjectOption.ts deleted file mode 100644 index 8ba131f..0000000 --- a/src/utils/getRefObjectOption/getRefObjectOption.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { RefObject } from 'react'; - -/** - * Type for a value that is optionally wrapped in a RefObject type - */ -export type RefObjectOption = RefObject | T; - -/** - * Type to get the value of a RefObjectOption - */ -export type RefObjectOptionValue> = T extends RefObject - ? U - : T; - -/** - * This function allows you to get the current value of a ref object, or the - * value as is. - */ -export function getRefObjectOptionValue(target: RefObjectOption): T | null { - return target !== null && typeof target === 'object' && 'current' in target - ? target.current - : target; -} diff --git a/src/utils/isRefObject/isRefObject.ts b/src/utils/isRefObject/isRefObject.ts index cab50bb..f6ce9d5 100644 --- a/src/utils/isRefObject/isRefObject.ts +++ b/src/utils/isRefObject/isRefObject.ts @@ -1,4 +1,4 @@ -import { type RefObject } from 'react'; +import type { RefObject } from 'react'; /** * Type guard to check if a value is a RefObject diff --git a/src/utils/getRefObjectOption/getRefObjectOption.mdx b/src/utils/unref/unref.mdx similarity index 58% rename from src/utils/getRefObjectOption/getRefObjectOption.mdx rename to src/utils/unref/unref.mdx index 556a65a..f859620 100644 --- a/src/utils/getRefObjectOption/getRefObjectOption.mdx +++ b/src/utils/unref/unref.mdx @@ -1,12 +1,12 @@ import { Meta } from '@storybook/blocks'; - + -# getRefObjectOption +# unref This module contains utilities for working with ref objects. -## `RefObjectOption` +## `Unreffable` Type for a value that is optionally wrapped in a RefObject type. This is useful for cases where a value may be passed as either a plain value or a RefObject containing the value. @@ -14,25 +14,25 @@ value may be passed as either a plain value or a RefObject containing the value. ### Example usage ```ts -type MyType = RefObjectOption; +type MyType = Unreffable; const plainValue = 'foo' satisfies MyType; // plain value const refObjectValue = { current: 'bar' } satisfies MyType; // RefObject containing value ``` -## `RefObjectOptionValue>` +## `Unref>` -Type to get the value of a RefObjectOption. If the input type is a RefObject, it returns the type -T. Otherwise, it returns the input type itself. +Type to get the value of an Unreffable object. If the input type is a RefObject, it returns the +type T. Otherwise, it returns the input type itself. ### Example usage ```ts -type MyType = RefObjectOption; -type MyValueType = RefObjectOptionValue; // string +type MyPlainType = Unref; // string +type MyRefObject = RefObject>; // string ``` -## `getRefObjectOptionValue(target: RefObjectOption): T | null` +## `unref(target: Unreffable): T | null` This function allows you to get the current value of a ref object, or the value as is. @@ -46,8 +46,8 @@ The current value of the ref object, or the input value if it is not a ref objec ```ts const myRefObject = { current: 'foo' }; -getRefObjectOptionValue(myRefObject); // 'foo' +unref(myRefObject); // 'foo' const myValue = 'bar'; -getRefObjectOptionValue(myValue); // 'bar' +unref(myValue); // 'bar' ``` diff --git a/src/utils/getRefObjectOption/getRefObjectOption.test.ts b/src/utils/unref/unref.test.ts similarity index 61% rename from src/utils/getRefObjectOption/getRefObjectOption.test.ts rename to src/utils/unref/unref.test.ts index 97d98a6..f6a389e 100644 --- a/src/utils/getRefObjectOption/getRefObjectOption.test.ts +++ b/src/utils/unref/unref.test.ts @@ -1,21 +1,23 @@ -import { getRefObjectOptionValue } from './getRefObjectOption.js'; +import { unref } from './unref.js'; -describe('getRefObjectOption', () => { +describe('unref', () => { it('should return the current value of the ref object if it exists', () => { const value = { current: 'hello' }; - const result = getRefObjectOptionValue(value); + const result = unref(value); expect(result).toEqual('hello'); }); it('should return the original value if it is not a ref object', () => { const value = 'world'; - const result = getRefObjectOptionValue(value); + const result = unref(value); expect(result).toEqual('world'); }); it('should return null if the input is null', () => { - expect(null).toBeNull(); + const result = unref(null); + + expect(result).toBeNull(); }); }); diff --git a/src/utils/unref/unref.ts b/src/utils/unref/unref.ts new file mode 100644 index 0000000..78784d6 --- /dev/null +++ b/src/utils/unref/unref.ts @@ -0,0 +1,19 @@ +import type { RefObject } from 'react'; +import { isRefObject } from '../isRefObject/isRefObject.js'; + +/** + * Type for a value that is optionally wrapped in a RefObject type + */ +export type Unreffable = RefObject | T; + +/** + * Type to get the value of a Unreffable + */ +export type Unref> = T extends RefObject ? U : T; + +/** + * This function returns the current value of a ref object, or the plain value. + */ +export function unref(target: Unreffable): T | null { + return isRefObject(target) ? target.current : target; +} From d0f3da02f0bdbfe63601d794dcff5bb8b25c7fcf Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Tue, 4 Apr 2023 12:03:32 +0200 Subject: [PATCH 5/6] Remove unnecessary useDocument and useWindow hooks --- src/hooks/useDocument/useDocument.mdx | 28 ----------------- src/hooks/useDocument/useDocument.test.tsx | 10 ------- src/hooks/useDocument/useDocument.ts | 9 ------ .../useEventListener.stories.mdx | 30 ++++++++++++++----- .../useEventListener.stories.tsx | 5 +--- src/hooks/useHasFocus/useHasFocus.ts | 7 ++--- src/hooks/useWindow/useWindow.mdx | 28 ----------------- src/hooks/useWindow/useWindow.test.ts | 10 ------- src/hooks/useWindow/useWindow.ts | 9 ------ src/index.ts | 2 -- src/utils/unref/unref.mdx | 4 +-- 11 files changed, 27 insertions(+), 115 deletions(-) delete mode 100644 src/hooks/useDocument/useDocument.mdx delete mode 100644 src/hooks/useDocument/useDocument.test.tsx delete mode 100644 src/hooks/useDocument/useDocument.ts delete mode 100644 src/hooks/useWindow/useWindow.mdx delete mode 100644 src/hooks/useWindow/useWindow.test.ts delete mode 100644 src/hooks/useWindow/useWindow.ts diff --git a/src/hooks/useDocument/useDocument.mdx b/src/hooks/useDocument/useDocument.mdx deleted file mode 100644 index 1bdf986..0000000 --- a/src/hooks/useDocument/useDocument.mdx +++ /dev/null @@ -1,28 +0,0 @@ -import { Meta } from '@storybook/blocks'; - - - -# useDocument - -This hook returns the Document instance when it's availbable in the environment. If the Document is -not available, it will return `undefined`. - -## Reference - -```ts -function useDocument(): Document | undefined; -``` - -## Usage - -```tsx -function DemoComponent(): ReactNode { - const document = useDocument(); - - useEventListener(document, 'focusin', (event) => { - // do something - }); - - return null; -} -``` diff --git a/src/hooks/useDocument/useDocument.test.tsx b/src/hooks/useDocument/useDocument.test.tsx deleted file mode 100644 index c28e9c6..0000000 --- a/src/hooks/useDocument/useDocument.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { useDocument } from './useDocument.js'; - -describe('useEventListener', () => { - it('Should return the document', () => { - const result = renderHook(useDocument); - - expect(result.result.current).toBeInstanceOf(Document); - }); -}); diff --git a/src/hooks/useDocument/useDocument.ts b/src/hooks/useDocument/useDocument.ts deleted file mode 100644 index ea3a617..0000000 --- a/src/hooks/useDocument/useDocument.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Returns undefined when in a server environment, otherwise returns document. - * - * Note: be careful with this hook as it returns a different value on on server - * and client. - */ -export function useDocument(): Document | undefined { - return typeof document === 'undefined' ? undefined : document; -} diff --git a/src/hooks/useEventListener/useEventListener.stories.mdx b/src/hooks/useEventListener/useEventListener.stories.mdx index 25eadb4..0c38cdd 100644 --- a/src/hooks/useEventListener/useEventListener.stories.mdx +++ b/src/hooks/useEventListener/useEventListener.stories.mdx @@ -10,28 +10,42 @@ removed when the component unmounts. ## Reference ```ts -export function useEventListener( - target: T | undefined, - event: K, - listener: (this: EventTarget, event: EventMap[K]) => void, - options?: boolean | EventListenerOptions, +function useEventListener( + targetOption: RefObject | T | null | undefined, + type: string, + listener: EventListener, + options?: boolean | AddEventListenerOptions, ): void; ``` ## Usage -## Server-size rendering +Using a RefObject to attach an event to a DOM element. + +```tsx +function MyComponent() { + const ref = useRef(null); + + useEventListener(ref, 'click', (event) => { + ... + }); + + return
...
; +} +``` + +### Server-size rendering The hook doesn't throw errors when the target is undefined to support server-side rendering. ```tsx -useEventListener(typeof document === 'undefined' ? undefined : document, 'focusin', (event) => { +useEventListener(globalThis.document, 'focusin', (event) => { ... }); ``` ```tsx -useEventListener(typeof window === 'undefined' ? undefined : window, 'resize', (event) => { +useEventListener(globalThis.window, 'resize', (event) => { ... }); ``` diff --git a/src/hooks/useEventListener/useEventListener.stories.tsx b/src/hooks/useEventListener/useEventListener.stories.tsx index c31b7ef..4e847bd 100644 --- a/src/hooks/useEventListener/useEventListener.stories.tsx +++ b/src/hooks/useEventListener/useEventListener.stories.tsx @@ -1,7 +1,6 @@ /* eslint-disable react/jsx-no-literals */ import type { StoryObj } from '@storybook/react'; import { useState, type ReactElement } from 'react'; -import { useDocument } from '../useDocument/useDocument.js'; import { useEventListener } from './useEventListener.js'; export default { @@ -11,9 +10,7 @@ export default { function DemoComponent(): ReactElement { const [text, setText] = useState>([]); - const document = useDocument(); - - useEventListener(document, 'focusin', (event) => { + useEventListener(globalThis.document, 'focusin', (event) => { // eslint-disable-next-line no-console setText((previous) => [ ...previous, diff --git a/src/hooks/useHasFocus/useHasFocus.ts b/src/hooks/useHasFocus/useHasFocus.ts index cfca9b1..ee94faf 100644 --- a/src/hooks/useHasFocus/useHasFocus.ts +++ b/src/hooks/useHasFocus/useHasFocus.ts @@ -1,5 +1,4 @@ import { useCallback, useState, type RefObject } from 'react'; -import { useDocument } from '../useDocument/useDocument.js'; import { useEventListener } from '../useEventListener/useEventListener.js'; export type FocusPseudoSelector = ':focus' | ':focus-visible' | ':focus-within'; @@ -22,10 +21,8 @@ export function useHasFocus( setState(matches()); }, [matches, setState]); - const document = useDocument(); - - useEventListener(document, 'focusin', onUpdate); - useEventListener(document, 'focusout', onUpdate); + useEventListener(globalThis.document, 'focusin', onUpdate); + useEventListener(globalThis.document, 'focusout', onUpdate); return state; } diff --git a/src/hooks/useWindow/useWindow.mdx b/src/hooks/useWindow/useWindow.mdx deleted file mode 100644 index 6201dde..0000000 --- a/src/hooks/useWindow/useWindow.mdx +++ /dev/null @@ -1,28 +0,0 @@ -import { Meta } from '@storybook/blocks'; - - - -# useWindow - -This hook returns the Window instance when it's availbable in the environment. If the Window is not -available, it will return `undefined`. - -## Reference - -```ts -function useWindow(): Window | undefined; -``` - -## Usage - -```tsx -function DemoComponent(): ReactNode { - const window = useWindow(); - - useEventListener(window, 'keydown', (event) => { - // do something - }); - - return null; -} -``` diff --git a/src/hooks/useWindow/useWindow.test.ts b/src/hooks/useWindow/useWindow.test.ts deleted file mode 100644 index 3c358c4..0000000 --- a/src/hooks/useWindow/useWindow.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { useWindow } from './useWindow.js'; - -describe('useWindow', () => { - it('Should return the window', () => { - const result = renderHook(useWindow); - - expect(result.result.current).toBe(window); - }); -}); diff --git a/src/hooks/useWindow/useWindow.ts b/src/hooks/useWindow/useWindow.ts deleted file mode 100644 index 4f3ce01..0000000 --- a/src/hooks/useWindow/useWindow.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Returns undefined when in a server environment, otherwise returns window. - * - * Note: be careful when using this hook, it returns a different value on on server - * and client. - */ -export function useWindow(): (Window & typeof globalThis) | undefined { - return typeof window === 'undefined' ? undefined : window; -} diff --git a/src/index.ts b/src/index.ts index b7db7ad..74c0ea0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ /* PLOP_ADD_EXPORT */ export * from './components/AutoFill/AutoFill.js'; export * from './hocs/ensuredForwardRef/ensuredForwardRef.js'; -export * from './hooks/useDocument/useDocument.js'; export * from './hooks/useEventListener/useEventListener.js'; export * from './hooks/useHasFocus/useHasFocus.js'; export * from './hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js'; @@ -17,7 +16,6 @@ export * from './hooks/useRegisterRef/useRegisterRef.js'; export * from './hooks/useResizeObserver/useResizeObserver.js'; export * from './hooks/useToggle/useToggle.js'; export * from './hooks/useUnmount/useUnmount.js'; -export * from './hooks/useWindow/useWindow.js'; export * from './utils/arrayRef/arrayRef.js'; export * from './utils/isRefObject/isRefObject.js'; export * from './utils/unref/unref.js'; diff --git a/src/utils/unref/unref.mdx b/src/utils/unref/unref.mdx index f859620..7d76f6e 100644 --- a/src/utils/unref/unref.mdx +++ b/src/utils/unref/unref.mdx @@ -22,8 +22,8 @@ const refObjectValue = { current: 'bar' } satisfies MyType; // RefObject contain ## `Unref>` -Type to get the value of an Unreffable object. If the input type is a RefObject, it returns the -type T. Otherwise, it returns the input type itself. +Type to get the value of an `Unreffable` object. If the input type is a `RefObject`, it returns +the type `T`. Otherwise, it returns the input type itself. ### Example usage From 39de13a6e597ae1b22559e664bcb357a0588e348 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Tue, 4 Apr 2023 12:23:40 +0200 Subject: [PATCH 6/6] Update src/utils/unref/unref.mdx Co-authored-by: Arjan van Wijk --- src/utils/unref/unref.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/unref/unref.mdx b/src/utils/unref/unref.mdx index 7d76f6e..c7936dd 100644 --- a/src/utils/unref/unref.mdx +++ b/src/utils/unref/unref.mdx @@ -29,7 +29,7 @@ the type `T`. Otherwise, it returns the input type itself. ```ts type MyPlainType = Unref; // string -type MyRefObject = RefObject>; // string +type MyRefObject = Unref>; // string ``` ## `unref(target: Unreffable): T | null`