react-to-print报错Argument appears to not be a ReactComponent
问题描述
使用react-to-print实现组件打印功能时,基础功能运行正常,按业务需求自定义组件后,触发打印抛出Argument appears to not be a ReactComponent. Keys:报错,常规检索未找到有效解决方案。
报错截图
现象:点击showcontent按钮可正常查看组件渲染内容,点击print按钮触发打印时抛出上述错误。
相关代码
待打印组件(ModalContent)
const ModalContent=React.forwardRef(({ ticketDetails, bookingHistory, movie }, ref)=>{ var op = movie.filter((e, i, arr) => { return e._id == bookingHistory.movieId }) const [invoiceSummary, setinvoiceSummary] = useState({ totalSeatCount: bookingHistory.seatSeletion.length, ticketPrice: op[0].ticketPrice, gstRate: op[0].gst, serviceCharge: op[0].serviceCharge }) const [summaryFinalOk, setsummaryFinalOk] = useState(true); useImperativeHandle(ref, () => ({ showTick() { setsummaryFinalOk(true); } })); return (<> <div className="card"> {summaryFinalOk ?<> <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52"> <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" /> <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" /> </svg> <h3>your Ticket has been Confirmed</h3></> : <> <div className="card-header bg-black"></div> <div className="card-body"> <div className="row"> <div className="row text-center"> <h3 className="text-uppercase text-center mt-3 invoice-h3" >Invoice</h3> </div> <div className="row mx-3"> <table className="table"> <thead> <tr> <th scope="col">Ticket Order Summary </th> <th scope="col">Amount</th> </tr> </thead> <tbody> <tr> <td>Ticket price</td> <td>{invoiceSummary.totalSeatCount} x {invoiceSummary.ticketPrice} = {invoiceSummary.totalSeatCount * invoiceSummary.ticketPrice}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> <tr> <td>Gst</td> <td>{invoiceSummary.gstRate} % ={(invoiceSummary.totalSeatCount * invoiceSummary.ticketPrice) * invoiceSummary.gstRate / 100}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> <tr> <td>Internet Handling</td> <td>{invoiceSummary.totalSeatCount} x{invoiceSummary.serviceCharge}={invoiceSummary.totalSeatCount * invoiceSummary.serviceCharge}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> </tbody> </table> </div> <hr></hr> </div> </div> </>} </div> </>); })
父组件(TicketBooking)
import React, { memo, useEffect, useState, useRef, useCallback } from 'react'; import {useReactToPrint} from 'react-to-print'; import ModalContent from './modalContent'; function TicketBooking({ }) { const tick=useRef(); //print the data const handlePrint = useReactToPrint({ content: () => tick.current }); const showcontent = ()=>{ } return ( <div className="container screen-design"> <button onclick={showcontent}>showcontent</button> <ModalContent ticketDetails={""} ref={tick} bookingHistory={[]} movie={""}></ModalContent> <button onclick={handlePrint}>print</button> </div>) } export default memo(TicketBooking);
报错原因
报错核心是react-to-print没有拿到合法的可打印DOM节点,由两个核心问题共同导致:
- 待打印组件
ModalContent最外层使用了React Fragment(<>...</>)作为根节点,Fragment不会生成真实DOM,ref无法直接绑定到实际渲染的DOM元素 - 使用
useImperativeHandle自定义了ref暴露的内容,仅向外暴露了showTick方法,完全覆盖了默认的DOM节点引用,react-to-print拿到的ref值只有自定义方法,识别为非法的React组件 - 附加语法问题:React事件绑定使用了原生小写
onclick,不符合React JSX语法规范,会导致按钮点击事件不生效
修复方案
- 移除
ModalContent最外层的Fragment包裹,将需要打印的.card节点作为组件直接返回的根元素 - 在
ModalContent内部单独创建ref绑定到打印根节点,通过useImperativeHandle同时向外暴露自定义方法和打印DOM节点 - 将JSX中的
onclick事件修改为React标准驼峰写法onClick
修复后代码
ModalContent组件
const ModalContent = React.forwardRef(({ ticketDetails, bookingHistory, movie }, ref) => { // 单独创建ref绑定实际打印的DOM节点 const printRootRef = useRef(null); var op = movie.filter((e, i, arr) => { return e._id == bookingHistory.movieId }) const [invoiceSummary, setinvoiceSummary] = useState({ totalSeatCount: bookingHistory.seatSeletion.length, ticketPrice: op[0].ticketPrice, gstRate: op[0].gst, serviceCharge: op[0].serviceCharge }) const [summaryFinalOk, setsummaryFinalOk] = useState(true); useImperativeHandle(ref, () => ({ // 保留原有自定义方法 showTick() { setsummaryFinalOk(true); }, // 额外暴露打印所需的DOM节点 printElement: printRootRef.current })); // 移除最外层Fragment,直接返回根DOM节点并绑定内部ref return ( <div className="card" ref={printRootRef}> {summaryFinalOk ? <> <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52"> <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" /> <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" /> </svg> <h3>your Ticket has been Confirmed</h3></> : <> <div className="card-header bg-black"></div> <div className="card-body"> <div className="row"> <div className="row text-center"> <h3 className="text-uppercase text-center mt-3 invoice-h3" >Invoice</h3> </div> <div className="row mx-3"> <table className="table"> <thead> <tr> <th scope="col">Ticket Order Summary </th> <th scope="col">Amount</th> </tr> </thead> <tbody> <tr> <td>Ticket price</td> <td>{invoiceSummary.totalSeatCount} x {invoiceSummary.ticketPrice} = {invoiceSummary.totalSeatCount * invoiceSummary.ticketPrice}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> <tr> <td>Gst</td> <td>{invoiceSummary.gstRate} % ={(invoiceSummary.totalSeatCount * invoiceSummary.ticketPrice) * invoiceSummary.gstRate / 100}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> <tr> <td>Internet Handling</td> <td>{invoiceSummary.totalSeatCount} x{invoiceSummary.serviceCharge}={invoiceSummary.totalSeatCount * invoiceSummary.serviceCharge}<i class="fa-solid fa-indian-rupee-sign"></i></td> </tr> </tbody> </table> </div> <hr></hr> </div> </div> </>} </div> ); })
父组件TicketBooking
import React, { memo, useEffect, useState, useRef, useCallback } from 'react'; import {useReactToPrint} from 'react-to-print'; import ModalContent from './modalContent'; function TicketBooking({ }) { const tick=useRef(); const handlePrint = useReactToPrint({ // 获取暴露的打印DOM节点 content: () => tick.current.printElement }); const showcontent = ()=>{ // 调用子组件暴露的方法 tick.current?.showTick() } return ( <div className="container screen-design"> {/* 修正事件名为驼峰写法 */} <button onClick={showcontent}>showcontent</button> <ModalContent ticketDetails={""} ref={tick} bookingHistory={[]} movie={""}></ModalContent> <button onClick={handlePrint}>print</button> </div>) } export default memo(TicketBooking);
额外提示:当前代码中传入
ModalContent的bookingHistory初始为空数组、movie初始为空字符串,组件初始化阶段读取嵌套属性会触发报错,建议给props传入合法初始值,或使用可选链?.做安全取值处理。
内容的提问来源于stack exchange,提问作者shanmuga priya
相关产品推荐
相关产品推荐

