javascript - ResizeObserver always return the same contentRect - Stack Overflow

admin2025-04-20  0

I'm trying to show a PDF in my page by using "react-pdf". I am now struggling with making it responsive. I am now using this custom hook to get the size of the contentRect:

import { useEffect, useState, useCallback } from "react";

interface Size {
  width: number;
  height: number;
}

const useResizeObserver = (targetRef: HTMLElement | null): Size => {
  const [size, setSize] = useState<Size>({ width: 0, height: 0 });

  const handleResize = useCallback((entries: ResizeObserverEntry[]) => {
    const [entry] = entries;
    if (entry) {
      setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    }
  }, []);

  useEffect(() => {
    if (!targetRef) return;

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef);

    return () => {
      resizeObserver.disconnect();
    };
  }, [targetRef, handleResize]);

  return size;
};

export default useResizeObserver;

This is my PDFViewer.tsx that is used in the page.tsx:

"use client";

import React, { useRef, useState } from "react";
import { Document, Page } from "react-pdf";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import { pdfjs } from "react-pdf";
import "react-pdf/dist/Page/TextLayer.css";
import type { PDFDocumentProxy } from 'pdfjs-dist';
import useResizeObserver from "@/hooks/useResizeObserver";

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.mjs",
  import.meta.url
).toString();

const PDFViewer: React.FC = () => {
  const [numPages, setNumPages] = useState<number>(0);
  const [pageNumber, setPageNumber] = useState<number>(1);
  const containerRef = useRef<HTMLDivElement | null>(null);

  const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerRef.current);

  const pdfUrl = "/docs/menu.pdf";

  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {
    setNumPages(nextNumPages);
  }

  return (
    <div className="flex flex-row left-2 mt-4 justify-center items-center w-full h-auto px-4">
      <div className="flex flex-1 font-generalRegular justify-right text-right text-primary text-6xl px-4">
        {" < "}
      </div>
      <div id="containerRef" ref={containerRef} className="flex flex-8 flex-col font-generalRegular justify-center text-center text-primary text-sm px-4">
        <p>Menu</p>
        <p>Page {pageNumber} of {numPages}</p>
        <p>Width: {pdfWidth}</p>
        <Document file={pdfUrl} onLoadSuccess={onDocumentLoadSuccess}>
          <Page pageNumber={pageNumber} width={pdfWidth}/>
        </Document>
      </div>
      <div className="flex flex-1 font-generalRegular justify-left text-left text-primary text-6xl px-4">
        {" > "}
      </div>
    </div>
  );
};

export default PDFViewer;

As you might notice, I've added a text element to print the width. Yet, it never changes. Even when I constantly resize the browser window. Can anybody tell me what went wrong here?

Thank you.

I'm trying to show a PDF in my page by using "react-pdf". I am now struggling with making it responsive. I am now using this custom hook to get the size of the contentRect:

import { useEffect, useState, useCallback } from "react";

interface Size {
  width: number;
  height: number;
}

const useResizeObserver = (targetRef: HTMLElement | null): Size => {
  const [size, setSize] = useState<Size>({ width: 0, height: 0 });

  const handleResize = useCallback((entries: ResizeObserverEntry[]) => {
    const [entry] = entries;
    if (entry) {
      setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    }
  }, []);

  useEffect(() => {
    if (!targetRef) return;

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef);

    return () => {
      resizeObserver.disconnect();
    };
  }, [targetRef, handleResize]);

  return size;
};

export default useResizeObserver;

This is my PDFViewer.tsx that is used in the page.tsx:

"use client";

import React, { useRef, useState } from "react";
import { Document, Page } from "react-pdf";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import { pdfjs } from "react-pdf";
import "react-pdf/dist/Page/TextLayer.css";
import type { PDFDocumentProxy } from 'pdfjs-dist';
import useResizeObserver from "@/hooks/useResizeObserver";

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.mjs",
  import.meta.url
).toString();

const PDFViewer: React.FC = () => {
  const [numPages, setNumPages] = useState<number>(0);
  const [pageNumber, setPageNumber] = useState<number>(1);
  const containerRef = useRef<HTMLDivElement | null>(null);

  const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerRef.current);

  const pdfUrl = "/docs/menu.pdf";

  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {
    setNumPages(nextNumPages);
  }

  return (
    <div className="flex flex-row left-2 mt-4 justify-center items-center w-full h-auto px-4">
      <div className="flex flex-1 font-generalRegular justify-right text-right text-primary text-6xl px-4">
        {" < "}
      </div>
      <div id="containerRef" ref={containerRef} className="flex flex-8 flex-col font-generalRegular justify-center text-center text-primary text-sm px-4">
        <p>Menu</p>
        <p>Page {pageNumber} of {numPages}</p>
        <p>Width: {pdfWidth}</p>
        <Document file={pdfUrl} onLoadSuccess={onDocumentLoadSuccess}>
          <Page pageNumber={pageNumber} width={pdfWidth}/>
        </Document>
      </div>
      <div className="flex flex-1 font-generalRegular justify-left text-left text-primary text-6xl px-4">
        {" > "}
      </div>
    </div>
  );
};

export default PDFViewer;

As you might notice, I've added a text element to print the width. Yet, it never changes. Even when I constantly resize the browser window. Can anybody tell me what went wrong here?

Thank you.

Share Improve this question edited Mar 2 at 19:11 Drew Reese 204k18 gold badges245 silver badges273 bronze badges asked Mar 2 at 12:07 Bawenang Rukmoko Pardian PutraBawenang Rukmoko Pardian Putra 1,4311 gold badge19 silver badges40 bronze badges 2
  • @KJ It's always 595 for me. – Bawenang Rukmoko Pardian Putra Commented Mar 2 at 15:45
  • But still, shouldn't the width be changed when I resized the windows? But this one didn't. – Bawenang Rukmoko Pardian Putra Commented Mar 2 at 16:18
Add a comment  | 

1 Answer 1

Reset to default 1

When you call useResizeObserver use pass it the value of containerRef.current, which is undefined. After the component renders, and calls useEffect, the value is still null, which skips the observer's init due to if (!targetRef) return;.

const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerRef.current);

Instead change the hook to accept the entire ref object, and after the render the current value would update:

const useResizeObserver = (targetRef: RefObject<HTMLElement | null>): Size => {
  const [size, setSize] = useState<Size>({ width: 0, height: 0 });

  useEffect(() => {
    const handleResize = (entries: ResizeObserverEntry[]) => {
      const [entry] = entries;

      if (entry) {
        setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
        });
      }
    };

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef.current);

    return () => {
      resizeObserver.disconnect();
    };
  }, []);

  return size;
};

Calling it:

const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerRef);

Running example (without TS):

const { useState, useEffect, useRef } = React;

const useResizeObserver = targetRef => {
  const [size, setSize] = useState({ width: 0, height: 0 });

  useEffect(() => {
    const handleResize = entries => {
      const [entry] = entries;

      if (entry) {
        setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
        });
      }
    };

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef.current);

    return () => {
      resizeObserver.disconnect();
    };
  }, []);

  return size;
};

const Demo = () => {
  const ref = useRef();
  
  const size = useResizeObserver(ref);
  
  console.log(size);
  
  return <div className="target" ref={ref} />
}

ReactDOM
  .createRoot(root)
  .render(<Demo />);
.target {
  height: 50vh;
}
<script src="https://cdnjs.cloudflare/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>


However, in certain cases, for example components that are added / removed dynamically, the ref might not be ready in time. In those cases the hook can have a state that holds the ref. The hook returns a function to set the ref, and when the ref updates, the useEffect is called with the updated ref:

const useResizeObserver = () => {
  const [targetRef, setTargetRef] = useState<HTMLElement | null>(null);
  const [size, setSize] = useState<Size>({ width: 0, height: 0 });

  const handleResize = useCallback((entries: ResizeObserverEntry[]) => {
    const [entry] = entries;
    if (entry) {
      setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    }
  }, []);

  useEffect(() => {
    if (!targetRef) return;

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef);

    return () => {
      resizeObserver.disconnect();
    };
  }, [targetRef, handleResize]);

  return {
    setTargetRef,
    size
  };
};

Usage:

const PDFViewer: React.FC = () => {
  const [numPages, setNumPages] = useState<number>(0);
  const [pageNumber, setPageNumber] = useState<number>(1);

  const { setTargetRef, size: { width: pdfWidth, height: pdfHeight }} = useResizeObserver();
  
  /* unrelated code **/

  return (
    /* unrelated code **/
      <div id="containerRef" ref={setTargetRef} className="classed">
    /* unrelated code **/
  );
};

Running example (without TS):

const { useState, useEffect, useCallback } = React;

const useResizeObserver = () => {
  const [targetRef, setTargetRef] = useState(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  const handleResize = useCallback(entries => {
    const [entry] = entries;
    if (entry) {
      setSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    }
  }, []);

  useEffect(() => {
    if (!targetRef) return;

    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(targetRef);

    return () => {
      resizeObserver.disconnect();
    };
  }, [targetRef, handleResize]);

  return {
    setTargetRef,
    size
  };
};

const Demo = () => {
  const { setTargetRef, size } = useResizeObserver();
  
  console.log(size);

  return <div className="target" ref={setTargetRef} />
}

ReactDOM
  .createRoot(root)
  .render(<Demo />);
.target {
  height: 50vh;
}
<script src="https://cdnjs.cloudflare/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745123163a286269.html

最新回复(0)