From 212e71789862a020b5fac42039d6b3cabe6f9556 Mon Sep 17 00:00:00 2001 From: Leroy Korterink Date: Tue, 16 May 2023 00:04:45 +0200 Subject: [PATCH] Don't recreate ResizeObserver on callback reference change and use Unreffable --- .../useResizeObserver/useResizeObserver.mdx | 24 ++++++++++++-- .../useResizeObserver.stories.tsx | 32 ++++++++++++------- .../useResizeObserver/useResizeObserver.ts | 29 ++++++++++++----- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/hooks/useResizeObserver/useResizeObserver.mdx b/src/hooks/useResizeObserver/useResizeObserver.mdx index 240a43f..d0dc7c5 100644 --- a/src/hooks/useResizeObserver/useResizeObserver.mdx +++ b/src/hooks/useResizeObserver/useResizeObserver.mdx @@ -4,17 +4,22 @@ import { Meta } from '@storybook/blocks'; # useResizeObserver -This hook allows you to add a ResizeObserver for an element and remove it when the component +This hook allows you to add a `ResizeObserver` to an element and remove it when the component unmounts. ## Reference ```ts -function useResizeObserver(ref: RefObject, callback: ResizeObserverCallback): void; +function useResizeObserver( + target: Unreffable, + callback: ResizeObserverCallback, +): void; ``` ## Usage +Using the `useResizeObserver` hook using a RefObject: + ```tsx function DemoComponent() { const ref = useRef(null); @@ -26,3 +31,18 @@ function DemoComponent() { return
; } ``` + +Using the `useResizeObserver` hook using state: + +```tsx +function DemoComponent() { + const [element, setElement] = useState(null); + + useResizeObserver(element, () => { + // eslint-disable-next-line no-console + console.log('Element resized'); + }); + + return
; +} +``` diff --git a/src/hooks/useResizeObserver/useResizeObserver.stories.tsx b/src/hooks/useResizeObserver/useResizeObserver.stories.tsx index 8537a8a..d024b21 100644 --- a/src/hooks/useResizeObserver/useResizeObserver.stories.tsx +++ b/src/hooks/useResizeObserver/useResizeObserver.stories.tsx @@ -1,24 +1,34 @@ +/* eslint-disable react-hooks/rules-of-hooks */ import type { StoryObj } from '@storybook/react'; -import { type ReactElement, useRef } from 'react'; +import { useRef, useState } from 'react'; import { useResizeObserver } from './useResizeObserver.js'; export default { title: 'hooks/useResizeObserver', }; -function DemoComponent(): ReactElement { - const ref = useRef(null); +export const UsingRefObject: StoryObj = { + render() { + const ref = useRef(null); - useResizeObserver(ref, () => { - // eslint-disable-next-line no-console - console.log('Element resized'); - }); + useResizeObserver(ref, () => { + // eslint-disable-next-line no-console + console.log('Element resized'); + }); - return
; -} + return
; + }, +}; -export const Demo: StoryObj = { +export const UsingState: StoryObj = { render() { - return ; + const [element, setElement] = useState(null); + + useResizeObserver(element, () => { + // eslint-disable-next-line no-console + console.log('Element resized'); + }); + + return
; }, }; diff --git a/src/hooks/useResizeObserver/useResizeObserver.ts b/src/hooks/useResizeObserver/useResizeObserver.ts index 446fad5..2b7cb88 100644 --- a/src/hooks/useResizeObserver/useResizeObserver.ts +++ b/src/hooks/useResizeObserver/useResizeObserver.ts @@ -1,24 +1,37 @@ -import { type RefObject, useEffect } from 'react'; +import { useEffect } from 'react'; +import { unref, type Unreffable } from '../../index.js'; +import { useRefValue } from '../useRefValue/useRefValue.js'; /** - * This hook allows you to add a ResizeObserver for an element and remove it + * This hook allows you to add a ResizeObserver to an element and remove it * when the component unmounts. * * @param ref - The ref to observe * @param callback - The callback to fire when the element resizes */ -export function useResizeObserver(ref: RefObject, callback: ResizeObserverCallback): void { +export function useResizeObserver( + target: Unreffable, + callback: ResizeObserverCallback, +): void { + const callbackRef = useRefValue(callback); + useEffect(() => { - const resizeObserverInstance = new ResizeObserver(callback); + const element = unref(target); - if (ref.current === null) { - throw new Error('`ref.current` is undefined'); + if (element === null) { + return; } - resizeObserverInstance.observe(ref.current); + const resizeObserverInstance = new ResizeObserver( + (entries: Array, observer: ResizeObserver) => { + callbackRef.current?.(entries, observer); + }, + ); + + resizeObserverInstance.observe(element); return () => { resizeObserverInstance.disconnect(); }; - }, [ref, callback]); + }, [callbackRef, target]); }