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

React集成PDFtron WebViewer报同一HTML元素创建多实例错误如何修复

问题原因
  • 核心问题是WebViewer的实例创建逻辑放在了依赖[files, viewerUrl]的useEffect中:每次files更新、或者用户切换下拉框修改viewerUrl时,都会重新调用WebViewer()创建新实例,全部挂载到同一个id为viewer的DOM元素上,直接触发重复实例报错。
  • 直接使用document.getElementById('viewer')获取DOM不符合React最佳实践,首次渲染时DOM可能还未挂载,会拿到空值。
  • 缺少实例清理逻辑,组件卸载时没有销毁WebViewer实例,可能造成内存泄漏和重复实例问题。
  • 原代码还存在语法错误:return中<div className="container">后直接跟了多余的</div>,同时将逻辑或||错写为位或|,都会导致运行异常。
修复方案
  1. 用useRef分别存储WebViewer实例和viewer的DOM引用,避免重复创建实例,也保证能正确获取到挂载的DOM节点。
  2. 拆分useEffect逻辑:只在组件首次挂载、DOM准备完成时创建一次WebViewer实例,后续切换PDF文件时直接调用实例的loadDocument方法加载新文件即可,不需要重复创建实例。
  3. 新增清理逻辑,组件卸载时销毁WebViewer实例,避免内存泄漏。

修复后的完整代码如下:

import { url } from "../../../../config.json";
import React, { useState, useEffect, useRef } from "react";
import { getProject } from "../../../../services/projectService";
import { useParams } from "react-router-dom";
import WebViewer from "@pdftron/webviewer";
import { getCurrentUser } from "../../../../services/userService";
import { Redirect } from "react-router-dom";
import { deleteImage } from "../../../../services/projectService";

const MyContracts = () => {
  const [project, setProject] = useState({});
  const [files, setFiles] = useState([]);
  const { id } = useParams();
  // 存储viewer DOM引用
  const viewerDiv = useRef(null);
  // 存储WebViewer实例
  const wvInstance = useRef(null);
  const user = getCurrentUser();
  const [viewerUrl, setViewerUrl] = useState(`${url}/files/testing.pdf`);
    
  useEffect(() => {
    getProject(id)
    .then(res => {
      setProject(res.data);
      setFiles(res.data.files.contracts);
    })
    .catch(error => console.log(error.message));
  }, [id]);
    
  // 只在首次挂载时创建一次WebViewer实例
  useEffect(() => {
    // 避免重复创建
    if (wvInstance.current || !viewerDiv.current) return;

    WebViewer(
      {
        path: `${url}/lib`,
        initialDoc: viewerUrl,
        fullAPI: true, 
      },
      viewerDiv.current
    ).then(instance => {
      // 存储实例到ref
      wvInstance.current = instance;
    });

    // 组件卸载时销毁实例
    return () => {
      if (wvInstance.current) {
        wvInstance.current.dispose();
        wvInstance.current = null;
      }
    };
  }, []);

  // 监听viewerUrl变化,切换加载的PDF,不需要重新创建实例
  useEffect(() => {
    if (!wvInstance.current) return;
    wvInstance.current.loadDocument(viewerUrl);
  }, [viewerUrl]);

  if (!user) return <Redirect to="/private-area/sign-in" />;
  if (user && (user.isAdmin || (user._id === project.userID)))
    return (
      <div className="container">
        {/********** PDF VIEWER ************/}
        <div className="web-viewer" ref={viewerDiv}></div>
        
        {/********** PDF Gallery ************/}
        {files !== undefined && (
          <>
            <h2 className="text-rtl h3Title mt-2">בחר קובץ</h2>
            <select
              id="select"
              className="col-12 text-rtl px-0"
              onChange={e => setViewerUrl(e.target.value)}>
              {files.map((file, index) => (
                <option value={`${url}${file.url}`} key={index}>
                  {file.name}
                </option>
              ))}
            </select>
          </>
        )}
      </div>
    );
  return null;
};
    
export default MyContracts;

内容的提问来源于stack exchange,提问作者David Yakin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 11:36:08