React props传值异常:InvoicePDF渲染表格仅显示表头无行数据
问题原因
核心问题是条件渲染时生成了两个完全独立的FormDataTable组件实例,实例间内部状态完全不互通:
isInvoicevisible为true时渲染的FormDataTable,是你实际操作添加行数据的实例,所有录入的行数据都存在这个实例内部的tableDatastate中- 切换到
isInvoicevisible为false时,你传给InvoicePDF的<FormDataTable />是全新挂载的独立实例,它的tableData初始值为空数组,也没有接收itemOpen、setitemOpen必要props,自然只能渲染出空表头,拿不到之前录入的行数据。
另外你原有代码里表格行的td顺序和表头th顺序不匹配,渲染后列会错位。
修复方案
方案1:提升状态到共同父组件(推荐)
把表格数据、行添加逻辑从FormDataTable内部上移到父组件NewInvoice中维护,作为唯一数据源分发给两个场景下的FormDataTable,从根源避免多实例状态不同步的问题。
- 修改
NewInvoice组件代码,上移状态:
const NewInvoice = () => { const [isInvoicevisible, setisInvoicevisible] = useState(true); const [itemOpen, setitemOpen] = useState(false); // 原FormDataTable内的表格状态提升到父组件 const [tableData, setTableData] = useState([]); // 行添加逻辑同步上移 const addRows = (data) => { const totalData = tableData.length; data.id = totalData + 1; setTableData([...tableData, data]); setitemOpen(false); }; return ( <> {isInvoicevisible ? ( <div> <FormDataTable itemOpen={itemOpen} setitemOpen={setitemOpen} tableData={tableData} addRows={addRows} /> </div> ) : ( <InvoicePDF childComponent={<FormDataTable tableData={tableData} itemOpen={false} />} address={address} fake={fake_logo} name={name} vat={Vat} subtotal={subtotal} invoiceDate={invoiceDate} invoiceNo={invoiceNo} discount={discount} /> )} </> ) }
- 修改
FormDataTable组件,移除内部状态,从props接收数据和方法,同时修正列顺序错位问题:
const FormDataTable = (props) => { const { itemOpen, tableData, addRows } = props; const [showForm, setshowForm] = useState(false); const tableRows = tableData.map((value, index) => { return ( <tr key={index}> <td>{value.item}</td> <td>{value.description}</td> <td>{value.quantity}</td> <td>{value.rate}</td> <td>{value.amount}</td> </tr> ); }); return ( <React.Fragment> <table className="table"> <thead> <tr> <th>Item</th> <th>Description</th> <th>Qty</th> <th>Rate</th> <th>Amount</th> </tr> </thead> <tbody>{tableRows}</tbody> </table> {itemOpen && <AddItem show={itemOpen} func={addRows} />} </React.Fragment> ) }
InvoicePDF组件不需要修改,保持原有代码即可。
方案2:避免组件实例被销毁(快速修复)
如果不想调整状态位置,可以不用条件渲染销毁/重建组件,改用CSS的display属性控制两个视图的显示隐藏,保证FormDataTable始终是同一个实例,内部状态不会丢失:
// NewInvoice 组件内 // 先通过ref获取编辑态的FormDataTable实例 const tableRef = useRef(null); return ( <> <div style={{display: isInvoicevisible ? 'block' : 'none'}}> <FormDataTable ref={tableRef} itemOpen={itemOpen} setitemOpen={setitemOpen} /> </div> <div style={{display: isInvoicevisible ? 'none' : 'block'}}> <InvoicePDF childComponent={tableRef.current} // 其余props保持不变 /> </div> </> )
该方案可维护性较差,仅适合临时快速修复场景,优先选择状态提升的方案。
内容的提问来源于stack exchange,提问作者user23424
相关产品推荐
相关产品推荐

