Home > Mobile >  React fabric.js
React fabric.js

Time:06-15

I am trying to combine react and fabricjs but I am stuck.

Here is my code

import React, { useState, useEffect, useRef  } from 'react';
import { fabric } from "fabric";

function App() {

  const [canvas, setCanvas] = useState('');

  useEffect(() => {
    setCanvas(initCanvas());
    
  }, []);

  const initCanvas = () => (
    new fabric.Canvas('canvas', {
      height: 800,
      width: 800,
      backgroundColor: 'pink' ,
      selection: false,
      renderOnAddRemove: true,
     
    })

  )

    canvas.on("mouse:over", ()=>{
      console.log('hello')
    })


  return (

    <div >
      <canvas id="canvas" />
    </div>

  );
}

export default App;

The problem is canvas.on as it causes the error 'Uncaught TypeError: canvas.on is not a function' Please tell me what am I doing wrong here

CodePudding user response:

During the initial render, your canvas variable is set to your initial state, '' from useState(''). It's not until after this that your useEffect will run, updating the state value.

Recommendation: Move your event handlers into the useEffect and use a ref instead of state for your canvas value. refs have the property of being directly mutable and not requiring a rerender for their new value to be available.

import React, { useState, useEffect, useRef  } from 'react';
import { fabric } from "fabric";

function App() {

  const canvas = useRef(null);

  useEffect(() => {
    canvas.current = initCanvas();

    canvas.current.on("mouse:over", () => {
      console.log('hello')
    });
    
    // destroy fabric on unmount
    return () => {
      canvas.current.dispose();
      canvas.current = null;
    };
  }, []);

  const initCanvas = () => (
    new fabric.Canvas('canvas', {
      height: 800,
      width: 800,
      backgroundColor: 'pink' ,
      selection: false,
      renderOnAddRemove: true,
    })
  );

  return (

    <div >
      <canvas id="canvas" />
    </div>

  );
}

export default App;

It's worth noting that if you don't need a reference to the canvas elsewhere in your component, you don't need to use state or a ref and can use a local variable within the useEffect.

useEffect(() => {
  const canvas = initCanvas();
  canvas.on("mouse:over", () => {
    console.log('hello')
  });

  // destroy fabric on unmount
  return () => {
    canvas.dispose();
  };
})

CodePudding user response:

Actually the problem is that you trying to call canvas.on when it is an empty string in canvas (initial state)

Since we are only need to create fabric.Canvas once, I would recommend to store instance with React.useRef

I created an example for you here:

--> https://codesandbox.io/s/late-cloud-ed5r6q?file=/src/FabricExample.js

Will also show the source of the example component here:

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

const FabricExample = () => {
  const fabricRef = React.useRef(null);
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const initFabric = () => {
      fabricRef.current = new fabric.Canvas(canvasRef.current);
    };

    const addRectangle = () => {
      const rect = new fabric.Rect({
        top: 50,
        left: 50,
        width: 50,
        height: 50,
        fill: "red"
      });

      fabricRef.current.add(rect);
    };

    const disposeFabric = () => {
      fabricRef.current.dispose();
    };

    initFabric();
    addRectangle();

    return () => {
      disposeFabric();
    };
  }, []);

  return <canvas ref={canvasRef} />;
};

export default FabricExample;

  • Related