Search code examples
reactjsfabricjs

React + fabric.js


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


Solution

  • 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 ref={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();
      };
    })