React-to-Print问题:更新后的State未在打印输出中体现
React-To-Print 打印不更新合并后账单条目的问题解决
问题原因
React的state更新是异步批量处理的——你在onBeforeGetContent里调用setRows(consolidateItemRows(rows))后,rows不会立刻更新,组件也不会马上完成重渲染。此时printableRef.current指向的还是合并前的旧DOM结构,打印自然不会显示最新的合并结果。
解决方案
方案1:等待组件重渲染后再打印
利用onBeforeGetContent支持返回Promise的特性,等待state更新完成、组件重渲染后再继续打印流程:
import { useState, useEffect, useRef } from 'react'; import useReactToPrint from 'react-to-print'; const MyComponent = () => { const [rows, setRows] = useState(/* 初始账单数据 */); const [isPreparingPrint, setIsPreparingPrint] = useState(false); const printableRef = useRef(); let resolvePrintPrepare; // 监听rows变化,当打印准备状态激活时,触发Promise完成 useEffect(() => { if (isPreparingPrint) { resolvePrintPrepare?.(); setIsPreparingPrint(false); } }, [rows, isPreparingPrint]); const handleBillPrint = useReactToPrint({ content: () => printableRef.current, onAfterPrint: () => { handleBillSave("success"); handleBillReset(); }, onBeforeGetContent: () => { // 更新为合并后的账单数据 setRows(consolidateItemRows(rows)); setIsPreparingPrint(true); // 返回Promise,等待重渲染完成 return new Promise(resolve => { resolvePrintPrepare = resolve; }); } }); // 其他组件逻辑... return ( <div> {/* 可打印区域 */} <div ref={printableRef}> {/* 账单列表渲染 */} {rows.map(row => <div key={row.id}>{row.name} × {row.quantity} = {row.amount}</div>)} </div> <button onClick={handleBillPrint}>打印账单</button> </div> ); };
方案2:直接使用合并后的数据打印,不依赖组件state
如果合并后的账单数据仅用于打印,不需要保留在组件state中,可以直接生成合并数据并传递给打印内容,绕开state异步更新的问题:
const MyComponent = () => { const [rows, setRows] = useState(/* 初始账单数据 */); const printableRef = useRef(); const handleBillPrint = useReactToPrint({ content: () => { // 直接生成合并后的账单数据 const mergedRows = consolidateItemRows(rows); // 动态更新可打印区域的内容 printableRef.current.innerHTML = mergedRows.map(row => `<div key="${row.id}">${row.name} × ${row.quantity} = ${row.amount}</div>` ).join(''); return printableRef.current; }, onAfterPrint: () => { handleBillSave("success"); handleBillReset(); // 恢复原账单内容(如果需要) printableRef.current.innerHTML = rows.map(row => `<div key="${row.id}">${row.name} × ${row.quantity} = ${row.amount}</div>` ).join(''); } }); return ( <div> <div ref={printableRef}> {rows.map(row => <div key={row.id}>{row.name} × {row.quantity} = {row.amount}</div>)} </div> <button onClick={handleBillPrint}>打印账单</button> </div> ); };
方案3:拆分更新与打印步骤
先更新state,等组件重渲染完成后再手动触发打印:
const MyComponent = () => { const [rows, setRows] = useState(/* 初始账单数据 */); const [triggerPrint, setTriggerPrint] = useState(false); const printableRef = useRef(); // 创建打印实例 const printInstance = useReactToPrint({ content: () => printableRef.current, onAfterPrint: () => { handleBillSave("success"); handleBillReset(); setTriggerPrint(false); } }); // 监听打印触发状态,自动执行打印 useEffect(() => { if (triggerPrint) { printInstance(); } }, [triggerPrint, printInstance]); // 先更新数据,再触发打印 const handleBillPrint = () => { setRows(consolidateItemRows(rows)); // 等待组件重渲染后触发打印 setTimeout(() => setTriggerPrint(true), 0); }; return ( <div> <div ref={printableRef}> {rows.map(row => <div key={row.id}>{row.name} × {row.quantity} = {row.amount}</div>)} </div> <button onClick={handleBillPrint}>打印账单</button> </div> ); };
内容的提问来源于stack exchange,提问作者Abhijeet
相关产品推荐
相关产品推荐

