React(NextJS)打印组件二次点击报错NotFoundError求助
解决NextJS中打印React组件第二次点击报错的问题
你遇到的问题是首次点击打印能正常预览PDF,第二次点击就抛出NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node错误,根源在于你直接操作document.body.innerHTML的方式破坏了React的DOM管理机制。
错误原因
- React通过虚拟DOM追踪真实DOM的变化,你直接替换
document.body.innerHTML会完全清除React管理的所有节点,包括Sheet组件的DOM结构 - 第一次打印后恢复
originalContents,页面看起来正常,但React的虚拟DOM和真实DOM已经不同步 - 第二次点击打印时,Sheet组件的状态变化触发React更新,此时React尝试操作已被篡改的DOM,找不到对应的子节点,从而抛出错误
解决方案
用你已经导入的react-to-print库,它专门处理React组件的打印,不会破坏React的DOM结构。具体修改如下:
修改后的完整代码
import { Divider } from "@tremor/react" import { useRef } from "react" import React from "react" import { useReactToPrint } from "react-to-print" import { Button } from "@/components/ui/button" import { Sheet, SheetContent, SheetHeader, SheetTrigger } from "@/components/ui/sheet" import { BuildingInformation } from "./components/building-information" import { EnergyEfficiency } from "./components/energy-efficiency" import { FinancialAnalysis } from "./components/financial-analysis" import { Intro } from "./components/Intro" import { Modernization } from "./components/modernization" export const DataReporting = () => { // 创建ref绑定到要打印的div const printRef = useRef(null) // 使用react-to-print创建打印函数 const handlePrint = useReactToPrint({ content: () => printRef.current, // 可选:打印后可添加回调,比如关闭Sheet(需根据Sheet组件API实现) onAfterPrint: () => { // 示例:若用useState控制Sheet显示,可在此处设置状态为false // setSheetOpen(false) } }) return ( <Sheet> <SheetTrigger> <Button variant={"ghost"}>Holen Sie sich Ihren Bericht</Button> </SheetTrigger> <SheetContent side={"bottom"} className='h-full m-auto overflow-y-auto print:overflow-y-auto sm:w-screen lg:w-fit' > <SheetHeader> <Button className='w-fit z-20 mr-auto bg-blue-700 hover:bg-blue-600' onClick={handlePrint}> Herunterladen </Button> <Divider /> </SheetHeader> {/* 把ref绑定到这个要打印的div */} <div ref={printRef}> <Intro /> <BuildingInformation /> <Modernization /> <EnergyEfficiency /> <FinancialAnalysis /> </div> </SheetContent> </Sheet> ) }
替代方案(原生JS方式,不推荐)
如果必须用原生JS实现,可以创建临时iframe承载打印内容,避免干扰主页面DOM:
const print = () => { const printContents = document.getElementById("div")?.innerHTML if (!printContents) return const iframe = document.createElement('iframe') iframe.style.display = 'none' document.body.appendChild(iframe) const doc = iframe.contentDocument || iframe.contentWindow.document doc.write(` <html> <head> <title>打印内容</title> <!-- 引入你的样式文件 --> <link rel="stylesheet" href="/path/to/your/styles.css"> </head> <body>${printContents}</body> </html> `) doc.close() iframe.contentWindow?.print() // 打印完成后移除iframe setTimeout(() => { document.body.removeChild(iframe) }, 1000) }
内容的提问来源于stack exchange,提问作者Ala Eddine Menai
相关产品推荐
相关产品推荐

