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?

1 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:

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Alexander Ross

Alexander Ross

Gaming, Esports & Interactive Media Writer

Alexander Ross has covered the video game industry for a decade, writing deep dives on game design, esports tournaments, VR developments, and gaming culture.

Share this article
Twitter Facebook Pinterest