如何获取p元素实际宽度以动态设置react-pdf的PDF宽度?
解决React中获取p元素宽度始终不变的问题
问题核心
你试图获取页面中p元素的宽度,用于动态设置react-pdf生成的PDF宽度,但无论输入内容长短,点击按钮打印的宽度值始终固定,没有随内容变化更新。
原因分析
- useEffect仅执行一次:你的
useEffect没有设置依赖数组,只会在组件首次挂载时计算一次宽度,之后props.preview(内容)、props.font(字体)等影响宽度的变量变化时,不会重新计算并更新width状态。 - 依赖旧状态值:点击按钮时打印的是state中存储的width,而这个值只在挂载时被赋值,后续内容变化后没有更新,所以始终显示初始值。
- 目标元素样式缺失:用于获取宽度的p元素仅设置了字体类,未明确设置字体大小,可能因继承样式导致内容变化时宽度无明显变化。
修复方案
1. 让useEffect随依赖项更新宽度
补全useEffect的依赖数组,确保影响元素宽度的变量变化时,重新计算宽度:
const Preview = (props) => { const ref = useRef(); const [width, setWidth] = useState(0); useEffect(() => { // 确保元素存在再获取宽度 if (ref.current) { setWidth(ref.current.offsetWidth); } }, [props.preview, props.font, props.sizeClass]); // 加入所有影响宽度的依赖项 // ... 其他代码 };
2. 点击时直接获取实时宽度
不需要依赖state存储宽度,点击按钮时直接从ref中读取当前元素的实时宽度,避免state更新不及时的问题:
const getDimHandler = (e) => { e.preventDefault(); if (ref.current) { console.log(ref.current.offsetWidth); // 直接获取最新宽度 } };
3. 确保目标p元素样式正确
给用于获取宽度的p元素设置和预览区一致的字体大小,保证内容变化时宽度能正确反映:
<p ref={ref} id={props.sizeClass} className={`${props.font} text-[8rem]`}> {props.preview} </p>
4. 处理自定义字体加载延迟
如果使用了自定义字体,字体加载完成前计算的宽度可能不准确,可以等待字体加载完成后再获取宽度:
useEffect(() => { const updateWidthAfterFontLoaded = async () => { await document.fonts.ready; // 等待所有字体加载完成 if (ref.current) { setWidth(ref.current.offsetWidth); } }; updateWidthAfterFontLoaded(); }, [props.preview, props.font, props.sizeClass]);
修改后的完整示例代码
const Preview = (props) => { const ref = useRef(); const [width, setWidth] = useState(0); useEffect(() => { const updateWidthAfterFontLoaded = async () => { await document.fonts.ready; if (ref.current) { setWidth(ref.current.offsetWidth); } }; updateWidthAfterFontLoaded(); }, [props.preview, props.font, props.sizeClass]); const MyDoc = () => { Font.register({ family: "broadsheet", src: broadsheet, }); Font.register({ family: "jost", src: jost, }); Font.register({ family: "texgyreheros", src: texgyreheros, }); return ( <Document> <Page size={[width, 20000]} // 使用动态获取的宽度 style={{ fontFamily: props.pdfFont, fontSize: props.size, }} > <View style={{ margin: "auto", lineHeight: "0" }}> <Text>{props.preview}</Text> </View> </Page> </Document> ); }; const getDimHandler = (e) => { e.preventDefault(); if (ref.current) { console.log(ref.current.offsetWidth); } }; return ( <> <Container className="bg-white p-5 mb-5"> <p className={`${props.font} text-[8rem]`}>{props.preview}</p> </Container> <p ref={ref} id={props.sizeClass} className={`${props.font} text-[8rem]`}> {props.preview} </p> <PDFDownloadLink document={<MyDoc />} fileName="somename.pdf" className="bg-violet-500 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded-full" > {({ blob, url, loading, error }) => loading ? "Preparing document..." : "Download" } </PDFDownloadLink> <button onClick={getDimHandler}>Get Dims</button> </> ); }; export default Preview;
内容的提问来源于stack exchange,提问作者JimboNeutronbo
相关产品推荐
相关产品推荐

