Search code examples
reactjsreact-hookshtml5-canvasjsx

UseEffect Rendering Twice


I am building an whiteboard application and to create the white board I was playing with html canvas element. I was trying to print a word on canvas but I really don't understand why but it is putting it twice on the screen `

 import React, { useEffect, useRef } from "react";

    const WhiteBoard = () => {
      const canvasid = useRef();

      useEffect(() => {
        const canvas = canvasid.current;
        const ctx = canvas.getContext("2d");

        ctx.font = "30px Impact";
        ctx.rotate(0.1);
        ctx.fillText("Awesome!", 50, 100);

        // Draw line under text
        var text = ctx.measureText("Awesome!");
        ctx.strokeStyle = "rgba(0,0,0,0.5)";
        ctx.beginPath();
        ctx.lineTo(50, 102);
        ctx.lineTo(50 + text.width, 102);
        ctx.stroke();
      }, []);

      return (
        <>
          <div>WhiteBoard</div>
          <canvas ref={canvasid} style={{ border: "1px solid red" }}></canvas>
        </>
      );
    };

    export default WhiteBoard;

`

I was expecting the code to print Awesome on the canvas once but It is being printed twice, I tested with debugger and It shows that useEffect is being run twice But I only want it to run once when it is mounted on the screen


Solution

  • Even though OP already solved his issue with my comment above, but removing StrictMode is actually not a good solution here.

    Quoting from the React official docs:

    We recommend wrapping your entire app in Strict Mode, especially for newly created apps. If you use a framework that calls createRoot for you, check its documentation for how to enable Strict Mode.

    So, what's the good solution other than removing StrictMode ?

    Well, you can use cleanup function for that.

    So given the OP question, the answer would be like:

     import React, { useEffect, useRef } from "react";
    
        const WhiteBoard = () => {
          const canvasid = useRef();
    
          useEffect(() => {
            const canvas = canvasid.current;
            const ctx = canvas.getContext("2d");
    
            ctx.font = "30px Impact";
            ctx.rotate(0.1);
            ctx.fillText("Awesome!", 50, 100);
    
            // Draw line under text
            var text = ctx.measureText("Awesome!");
            ctx.strokeStyle = "rgba(0,0,0,0.5)";
            ctx.beginPath();
            ctx.lineTo(50, 102);
            ctx.lineTo(50 + text.width, 102);
            ctx.stroke();
            
            // add this
            return () => {ctx.reset()}
          }, []);
    
          return (
            <>
              <div>WhiteBoard</div>
              <canvas ref={canvasid} style={{ border: "1px solid red" }}></canvas>
            </>
          );
        };
    
        export default WhiteBoard;
    

    Although i didn't test the code, it should be clear that cleanup function will fix the rendering twice issue.