Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] useResizeObserver when component dom remove and rerender ( like dialog ), size not update #632

Open
hxdyj opened this issue Aug 20, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@hxdyj
Copy link

hxdyj commented Aug 20, 2024

Describe the bug

useResizeObserver when component dom remove and rerender ( like dialog ), size not refresh

To Reproduce

windows chrome

Expected behavior

when useResizeObserver ref dom remove and while readd to document need update size.
or export creatResizeObserver and destroyResizeObserver function give to developer to control.
like below:

function useResizeObserver(options) {
	const { ref, box = "content-box" } = options;
	const [{ width, height }, setSize] = useState(initialSize);
	const isMounted = useIsMounted();
	const previousSize = useRef({ ...initialSize });
	const onResize = useRef(void 0);
	onResize.current = options.onResize;
	const observerRef = useRef(null)
	const createResizeObserver = useCallback(() => {
		if (!ref.current)
			return;
		if (typeof window === "undefined" || !("ResizeObserver" in window))
			return;
		observerRef.current = new ResizeObserver(([entry]) => {
			const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize";
			const newWidth = extractSize(entry, boxProp, "inlineSize");
			const newHeight = extractSize(entry, boxProp, "blockSize");
			const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight;
			if (hasChanged) {
				const newSize = { width: newWidth, height: newHeight };
				previousSize.current.width = newWidth;
				previousSize.current.height = newHeight;
				if (onResize.current) {
					onResize.current(newSize);
				} else {
					if (isMounted()) {
						setSize(newSize);
					}
				}
			}
		});
		observerRef.current.observe(ref.current, { box });
	}, [isMounted, observerRef, box, ref])

	const destroyResizeObserver = useCallback(() => {
		if (observerRef.current) {
			observerRef.current.disconnect();
			previousSize.current = initialSize;
			observerRef.current = null;
		}
	}, [observerRef])

	useEffect(() => {
		createResizeObserver()
		return () => {
			destroyResizeObserver()
		}
	}, [box, ref, isMounted]);

	return { width, height, createResizeObserver, destroyResizeObserver };  // Notice: here add  createResizeObserver, destroyResizeObserver 
}

Additional context

No response

@hxdyj hxdyj added the bug Something isn't working label Aug 20, 2024
@hxdyj hxdyj changed the title [BUG] useResizeObserver when component dom remove and rerender ( like dialog ), size not refresh [BUG] useResizeObserver when component dom remove and rerender ( like dialog ), size not update Aug 20, 2024
@farzadsoltani
Copy link

Did you figure out a fix for this?

@hxdyj
Copy link
Author

hxdyj commented Oct 24, 2024

@farzadsoltani I am patch package by below diff, add destroyResizeObserver function to control.

diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..00c86b5d74a94f2de43efd2ca25111e3d79fd205
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+	"editor.formatOnSave": false
+}
diff --git a/dist/index.d.ts b/dist/index.d.ts
index 3d80dda3a48539d9dac356a1fe8b212f7e75ac56..a9ed7d296f8b9a51a2edcf339c0ec9c05f44ad62 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -665,7 +665,10 @@ type UseResizeObserverOptions<T extends HTMLElement = HTMLElement> = {
  * <div ref={myRef}>Hello, world!</div>
  * ```
  */
-declare function useResizeObserver<T extends HTMLElement = HTMLElement>(options: UseResizeObserverOptions<T>): Size;
+declare function useResizeObserver<T extends HTMLElement = HTMLElement>(options: UseResizeObserverOptions<T>): Size&{
+    createResizeObserver: () => void
+	destroyResizeObserver: () => void
+};
 
 /**
  * The hooks options.
diff --git a/dist/index.js b/dist/index.js
index dc0b5e680c83fcc8b9bf51c35e9de2ba500fa6fa..7b9358584f35e6d3aba40b78bfc72a3022ce10b2 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -605,41 +605,55 @@ var initialSize = {
   height: void 0
 };
 function useResizeObserver(options) {
-  const { ref, box = "content-box" } = options;
-  const [{ width, height }, setSize] = useState(initialSize);
-  const isMounted = useIsMounted();
-  const previousSize = useRef({ ...initialSize });
-  const onResize = useRef(void 0);
-  onResize.current = options.onResize;
-  useEffect(() => {
-    if (!ref.current)
-      return;
-    if (typeof window === "undefined" || !("ResizeObserver" in window))
-      return;
-    const observer = new ResizeObserver(([entry]) => {
-      const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize";
-      const newWidth = extractSize(entry, boxProp, "inlineSize");
-      const newHeight = extractSize(entry, boxProp, "blockSize");
-      const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight;
-      if (hasChanged) {
-        const newSize = { width: newWidth, height: newHeight };
-        previousSize.current.width = newWidth;
-        previousSize.current.height = newHeight;
-        if (onResize.current) {
-          onResize.current(newSize);
-        } else {
-          if (isMounted()) {
-            setSize(newSize);
-          }
-        }
-      }
-    });
-    observer.observe(ref.current, { box });
-    return () => {
-      observer.disconnect();
-    };
-  }, [box, ref, isMounted]);
-  return { width, height };
+	const { ref, box = "content-box" } = options;
+	const [{ width, height }, setSize] = useState(initialSize);
+	const isMounted = useIsMounted();
+	const previousSize = useRef({ ...initialSize });
+	const onResize = useRef(void 0);
+	onResize.current = options.onResize;
+	const observerRef = useRef(null)
+	const createResizeObserver = useCallback(() => {
+		if (!ref.current)
+			return;
+		if (typeof window === "undefined" || !("ResizeObserver" in window))
+			return;
+		observerRef.current = new ResizeObserver(([entry]) => {
+			const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize";
+			const newWidth = extractSize(entry, boxProp, "inlineSize");
+			const newHeight = extractSize(entry, boxProp, "blockSize");
+			const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight;
+			if (hasChanged) {
+				const newSize = { width: newWidth, height: newHeight };
+				previousSize.current.width = newWidth;
+				previousSize.current.height = newHeight;
+				if (onResize.current) {
+					onResize.current(newSize);
+				} else {
+					if (isMounted()) {
+						setSize(newSize);
+					}
+				}
+			}
+		});
+		observerRef.current.observe(ref.current, { box });
+	}, [isMounted, observerRef, box, ref])
+
+	const destroyResizeObserver = useCallback(() => {
+		if (observerRef.current) {
+			observerRef.current.disconnect();
+			previousSize.current = initialSize;
+			observerRef.current = null;
+		}
+	}, [observerRef])
+
+	useEffect(() => {
+		createResizeObserver()
+		return () => {
+			destroyResizeObserver()
+		}
+	}, [box, ref, isMounted]);
+
+	return { width, height, createResizeObserver, destroyResizeObserver };
 }
 function extractSize(entry, box, sizeType) {
   if (!entry[box]) {

@thejasonxie
Copy link

Easiest solution I found :

modify this line by using ref?.current.

it starts working after user clicks on the dialog which is okay in my case. I'm using shadcn dialog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants