I have a React component that renders a canvas and draws polygons on it:
function Plot(props) {
const [localPlot, setLocalPlot] = useState(props.plot);
...
useEffect(() => {
// here we get the Canvas context and draw polygons to it
}, [localPlot]);
return (
<>
<div
style={{
resize: "both",
border: "1px solid #32a1ce",
overflow: "auto",
}}
ref={ref}
>
<canvas
style={{ border: "thick solid #32a1ce" }}
className="canvas"
id={`canvas-${props.plotIndex}`}
/>
</div>
);
Now I want to allow the user to be able to resize the canvas, so I’ve made that possible with the div around it and resize: "both". Im using the library react-resize-detector library to detect when the div has been resized:
function Plot(props) {
const [localPlot, setLocalPlot] = useState(props.plot);
...
useEffect(() => {
// here we get the Canvas context and draw polygons to it
}, [localPlot]);
const onResize = useCallback((w, h) => {
// on resize logic
console.log("in onResize, w, h is ", w, h);
}, []);
const { width, height, ref } = useResizeDetector({
onResize,
});
return (
<>
<div
style={{
resize: "both",
border: "1px solid #32a1ce",
overflow: "auto",
}}
ref={ref}
>
<canvas
style={{ border: "thick solid #32a1ce" }}
className="canvas"
id={`canvas-${props.plotIndex}`}
</div>
);
The problem is that since I’ve added this, the canvas is blank. I believe this is because onResize is called after the render, and, somehow, wipes everything on the canvas. When I change to:
const { width, height, ref } = useResizeDetector({
handleHeight: false,
refreshMode: 'debounce',
refreshRate: 1000,
onResize
});
I see the polygons on the canvas for a second, before they are wiped. What am I doing wrong?
Advertisement
Answer
You can use a combination of state and effect to repaint canvas when it is resized.
Here’s a simplified example:
function Plot() {
const onResize = useCallback((w, h) => {
setSize({ w, h });
}, []);
const canvasRef = useRef();
const { ref } = useResizeDetector({ onResize });
const [size, setSize] = useState({ w: 300, h: 300 });
useEffect(() => {
const ctx = canvasRef.current.getContext("2d");
ctx.fillStyle = "green";
ctx.font = "18px serif";
ctx.fillText(`${size.w} x ${size.h}`, 10, 50);
}, [size]);
return (
<div id="resizer" ref={ref}>
{/* subtract 10 for the resize corner in the enclosing div */}
<canvas ref={canvasRef} width={size.w - 10} height={size.h - 10} />
</div>
);
}