Search code examples
reactjscanvasfabricjs

Fabricjs creates multiple nested canvases in a react component when adding new Text


I have been trying to create react functional component to be able to add a few texts inside the canvas. Even though the canvas renders the texts, you cannot move them since they are different unwanted nested canvases. In another word, any time you hit the add a new canvas is created which is not wanted. I have no idea what causes this nested canvases. Any help is highly appreciated.

here is the implementation

https://codesandbox.io/s/thirsty-mountain-kwyw1

the main component holds the list of texts in the state

import React, { useState } from "react";
import "./styles.css";
import MyCanvas from "./MyCanvas";

const App = () => {
  const fabric = window.fabric;
  const [controls, setControl] = useState([]);

  const add = () => {
    var text = new fabric.IText(
      "Sample Text " +
        new Date()
          .getTime()
          .toString()
          .substr(5),
      {
        fontFamily: "arial black",
        left: 100,
        top: 100,
        myid: new Date()
          .getTime()
          .toString()
          .substr(5),
        objecttype: "text"
      }
    );
    setControl([...controls, text]);
  };

  return (
    <div className="App">
      <button onClick={add}>Add</button>
      <MyCanvas controls={controls} />
    </div>
  );
};

export default App;

And this is the canvas component

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

const MyCanvas = ({ controls }) => {
  const canvas = new fabric.Canvas("canvas-element");

  useEffect(() => {
    if (!controls) return;

    // const canvas = new fabric.Canvas(ref.current.id);

    controls.map(item => {
      canvas.add(item).setActiveObject(item);
    });
  });

  return <canvas id="canvas-element" width={600} height={400} />;
};

export default MyCanvas;

Solution

  • I found the issue. Since the new fabric.Canvas("canvas-element") is a constructor, on every render it creates a new element and appends it to the DOM. To prevent this unwanted behaviour either you should check if the Canvas is already in the DOM tree or use alternative approaches like the one described in this post: How can I use Fabric.js with React?