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. ref
s 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;