Skip to content

Detect an element has been resized by a user with React

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?

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

You can see a complete demo here:

Edit