I'm looking for a way to have a component render all of it's children, measure their heights, then modify the layout based on those heights. For example, say I have a Document component that contains several (and unknown quantity) Paragraph components. I want the Document component to be able to measure those Paragraphs and insert a horizontal rule where a page break would go.
Before layout:
<Document pageSize="A4" layout="portrait">
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<Paragraph text="some long text" />
</Document>
After layout:
<Document pageSize="A4" layout="portrait">
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<hr /><!-- End of page 1 -->
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<Paragraph text="some long text" />
<hr /><!-- End of page 2 -->
<Paragraph text="some long text" />
</Document>
I can get each Paragraph component measure itself using the useClientRect()
hook from the Hooks FAQ, but I don't know how to get the Document component to access that height. Any advice would be greatly appreciated!
I would also like to clarify that I'm looking to provide visual feedback on screen about where page breaks will be when printing, akin to the feedback provided by Google Docs or MS Word.
This problem kept me up all night. But I think I found a solution that should work for you.
Essentially, you need to give each Paragraph
component a ref
. The ref
will have info on the height on the entire component. You then just need to pass that height back-up to the Parent
component that contains the Document
component.
Upon receiving all the heights from the Paragraphs, you can calculate a runningHeight
to dynamically add your PageBreaks
. In this case, page-breaks are just <hr>
tags that are conditionally added within each Paragraph
component.
See working sandbox: https://codesandbox.io/s/dynamically-adding-pagebreaks-fjvfl
Main code:
App.js
import React from "react";
import ReactDOM from "react-dom";
import Document from "./Document";
import Paragraph from "./Paragraph";
import data from "./data.js";
import "./styles.css";
class App extends React.Component {
state = {
pageBreak: 500,
heights: [],
breaks: []
};
updateHeight = height => {
this.setState(
prevState => {
return {
heights: [...prevState.heights, height]
};
},
() => {
this.setBreaks();
}
);
};
setBreaks = () => {
const { heights, pageBreak } = this.state;
const breaks = [];
let runningHeights = [];
let runningHeight = 0;
heights.forEach((height, index) => {
if (index === 0) {
runningHeight = height;
runningHeights.push(runningHeight);
if (runningHeight >= pageBreak) {
breaks.push(true);
} else {
breaks.push(false);
}
} else if (index > 0) {
if (runningHeights[index - 1] < pageBreak) {
runningHeight = runningHeights[index - 1] + height;
runningHeights.push(runningHeight);
if (runningHeight >= pageBreak) {
breaks.push(true);
} else {
breaks.push(false);
}
} else {
runningHeight = height;
runningHeights.push(runningHeight);
if (runningHeight >= pageBreak) {
breaks.push(true);
} else {
breaks.push(false);
}
}
}
});
this.setState({
breaks: breaks
});
};
render() {
const { breaks } = this.state;
return (
<div className="App">
<Document>
{data.map((item, index) => {
return (
<Paragraph
text={item.text}
updateHeight={this.updateHeight}
break={breaks[index]}
/>
);
})}
</Document>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Paragraph.js
import React, { useEffect } from "react";
const Paragraph = ({ text, updateHeight, ...props }) => {
const paragraphRef = React.createRef();
useEffect(() => {
updateHeight(paragraphRef.current.offsetHeight);
}, []);
return (
<div ref={paragraphRef}>
{text} {props.break && <hr />}
</div>
);
};
export default Paragraph;
Document.js
import React from "react";
const Document = props => {
return <div>{props.children}</div>;
};
export default Document;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With