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> ); }