Re-rendering using React.StrictMode and fabric.js's Canvas instance
P粉659378577
P粉659378577 2023-09-21 23:48:52
0
1
756

I'm creating a fabric canvas and buttons that instantiate shapes that should be selectable. I don't understand why my component is re-rendered twice in the following situation. Therefore, my fabric shape cannot be selected. However, when I remove the from my index.tsx file, the rendering only happens once and my shape is selectable. I could remove the , but I don't think that's the best solution. Here is a demo:

const { Fragment, StrictMode, useEffect, useRef } = React; const { createRoot } = ReactDOM; const styles = {}; const CanvasComponent = ({ id }) => { const canvasRef = useRef(null); useEffect(() => { console.log('init canvas'); // displayed twice with  canvasRef.current = initCanvas(); }, []); const initCanvas = () => ( canvasRef.current = new fabric.Canvas(`canvas-${id}`, { width: 800, height: 400, }) ); const addShape = (shapeType: string) => { let shape: fabric.Object; switch (shapeType) { case 'circle': shape = new fabric.Circle({ radius: 30, fill: 'red', left: 100, top: 100 }); break; case 'rectangle': shape = new fabric.Rect({ width: 60, height: 70, fill: 'green', left: 100, top: 100 }); break; default: return; } canvasRef.current.add(shape); }; return ( 
); } function StrictModeEnabled() { return

Strict Mode Enabled

; } function StrictModeDisabled() { return

Strict Mode Disabled

; } const strictModeEnabledRoot = createRoot(document.getElementById("strict-mode-enabled")); strictModeEnabledRoot.render(); const strictModeDisabledRoot = createRoot(document.getElementById("strict-mode-disabled")); strictModeDisabledRoot.render();
   

P粉659378577
P粉659378577

reply all (1)
P粉850680329

question

Why useEffect runs twice in React and how to deal with it?This question has a good answer describing why this happens and a general solution.

solution

In your case you need to clean up the instantiated canvas. I'm not familiar with Fabric, but from reading the documentation, thedisposemethodseems appropriate:

You need to return a function fromuseEffectthat calls the above method. As you can see from the linked question, it's good practice to return a function that does the cleanup fromuseEffect. There is also a working example below:

const { Fragment, StrictMode, useEffect, useRef } = React; const { createRoot } = ReactDOM; const styles = {}; const CanvasComponent = ({ id }) => { const canvasRef = useRef(null); useEffect(() => { console.log('init canvas'); // displayed twice with  canvasRef.current = initCanvas(); return () => canvasRef.current.dispose(); }, []); const initCanvas = () => ( canvasRef.current = new fabric.Canvas(`canvas-${id}`, { width: 800, height: 400, }) ); const addShape = (shapeType: string) => { let shape: fabric.Object; switch (shapeType) { case 'circle': shape = new fabric.Circle({ radius: 30, fill: 'red', left: 100, top: 100 }); break; case 'rectangle': shape = new fabric.Rect({ width: 60, height: 70, fill: 'green', left: 100, top: 100 }); break; default: return; } canvasRef.current.add(shape); }; return ( 
); } function StrictModeEnabled() { return

Strict Mode Enabled

; } const strictModeEnabledRoot = createRoot(document.getElementById("strict-mode-enabled")); strictModeEnabledRoot.render();
   
    Latest Downloads
    More>
    Web Effects
    Website Source Code
    Website Materials
    Front End Template
    About us Disclaimer Sitemap
    php.cn:Public welfare online PHP training,Help PHP learners grow quickly!