You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ResizeObserver始终返回相同contentRect,react-pdf响应式失效排查

问题原因及解决方案

核心问题

你遇到的宽度值不更新问题,根源在于**useRef的current属性变化不会触发组件重新渲染**,加上直接传递containerRef.current给useResizeObserver,导致钩子无法正确监听DOM元素的大小变化:

  1. 组件首次渲染时,containerRef.current为null,调用useResizeObserver(null),此时钩子内部的useEffect因目标元素不存在,不会创建ResizeObserver监听。
  2. 组件挂载后,containerRef.current被赋值为DOM元素,但useRef的current变化不会触发组件重新渲染,因此useResizeObserver不会被再次调用,钩子永远不会创建监听,宽度值始终保持初始的0。

解决方案

以下两种修复方式选其一即可:

方式一:改用回调Ref + useState跟踪DOM元素

修改PDFViewer组件,用useState存储DOM元素,配合回调Ref触发组件重渲染:

"use client";

import React, { 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);
  // 用useState存储DOM元素,回调Ref赋值时触发组件重渲染
  const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);

  // 传递存储的DOM元素给钩子
  const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerElement);

  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>
      {/* 使用回调Ref将元素存入state */}
      <div id="containerRef" ref={setContainerElement} 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>
        {/* 初始宽度为0时避免渲染Page组件,防止异常 */}
        {pdfWidth > 0 && (
          <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;

方式二:修改useResizeObserver钩子接收RefObject

调整自定义钩子,让它接收useRef返回的对象本身,而非current值,内部自动监听current的变化:

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

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

const useResizeObserver = (targetRef: RefObject<HTMLElement>): Size => {
  const [size, setSize] = useState<Size>({ width: 0, height: 0 });
  // 存储当前监听的元素,避免重复监听
  const observedElementRef = useRef<HTMLElement | null>(null);

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

  useEffect(() => {
    const currentElement = targetRef.current;
    // 如果元素未变化,直接返回
    if (currentElement === observedElementRef.current) return;

    // 断开之前的监听
    if (observedElementRef.current) {
      const prevObserver = new ResizeObserver(() => {});
      prevObserver.disconnect();
    }

    observedElementRef.current = currentElement;

    if (!currentElement) return;

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

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

  return size;
};

export default useResizeObserver;

然后修改PDFViewer中的调用,传递containerRef对象:

const containerRef = useRef<HTMLDivElement | null>(null);
// 传递ref对象而非current值
const { width: pdfWidth, height: pdfHeight } = useResizeObserver(containerRef);

额外优化

为避免初始宽度为0时Page组件渲染异常,建议添加条件渲染(如方式一中的{pdfWidth > 0 && ...}),确保宽度有效后再渲染PDF内容。

内容的提问来源于stack exchange,提问作者Bawenang Rukmoko Pardian Putra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 01:39:51