fabricjs-react icon indicating copy to clipboard operation
fabricjs-react copied to clipboard

Is it possible to use this in a hook?

Open subtext916 opened this issue 1 year ago • 6 comments

Hello, I have been exploring this solution and wanted to wrap it in my own hook to eliminate the "fabricjs" specific stuff and create a more generic "useCanvas" hook which returns a Canvas (instance of the FabricJSCanvas) and some methods to work with it, which all use the "editor" returned from the fabricjs-react hook. I am finding that this approach does not work because if I make a change to my App.tsx parent component, which triggers a re-render (hot deploy), the whole thing crashes with the error: TypeError: Cannot read properties of null (reading 'clearRect')

I have tried adding a useEffect return function to call editor.canvas.dispose() editor.canvas.off() editor.canvas.clear() and I cannot find a way around this problem. Is this a problem? Am I using this hook wrong? Any advice would be appreciated.

subtext916 avatar Jul 22 '24 05:07 subtext916

To give an example of what I am trying to do: A simplified example of my hook:

const useCanvas = ({ className = 'canvas' }) => {
const [initialized, setInitialized] = useState(false)
   const { selectedObjects, editor, onReady: onReadyOriginal } = useFabricJSEditor()
   const getCanvasObjects = useCallback(() => {
      return editor?.canvas?.getObjects()
   }, [editor])
   
   useEffect(() => { 
      if (editor?.canvas && !initialized) setInitialized(true)
   }, [initialized, editor, onReady])
   
   const Canvas = useMemo(() => {
    return () => <FabricJSCanvas className={className} onReady={onReady} />;
  }, [className, onReady]);
  
  return {
    Canvas,
    ready: initialized,
    getCanvasObjects
  }
   
}
export default useCanvas

subtext916 avatar Jul 22 '24 05:07 subtext916

@subtext916 hi can you please share code when is failing on clearRect

asotog avatar Jul 22 '24 16:07 asotog

Exact details... React project with the following files: tsconfig.json

index.tsx:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
    <App />
);

App.tsx:

import React from 'react'
import useCanvas from './useCanvas'

function App() {
  const { Canvas } = useCanvas({
    className: 'canvas'
  })

  return (
    <div className="App">
      <Canvas />
    </div>
  )
}

export default App

useCanvas.tsx:

import React, { useEffect, useMemo, useCallback, useState, useRef } from 'react'
import { FabricJSCanvas, useFabricJSEditor } from 'fabricjs-react'
import { fabric } from 'fabric'

interface CanvasProps {
  className?: string
}

const useCanvas = ({ className = 'canvas' }: CanvasProps) => {
  const editorRef = useRef<any>(null)
  const [initialized, setInitialized] = useState(false)
  const { editor, onReady: onReadyOriginal } = useFabricJSEditor()

  const onReady = useCallback(onReadyOriginal, []) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
      if (editor?.canvas) {
        if (!initialized) {
          setInitialized(true)
          editorRef.current = editor
        }
      }
  }, [initialized, editor, onReady])


  /**
   * Main canvas component
   */
  const Canvas = useMemo(() => {
    return () => <FabricJSCanvas className={className} onReady={onReady} />;
  }, [className, onReady]);

  useEffect(() => {
    return () => {
      editorRef.current?.canvas.off()
      editorRef.current?.canvas.clear()
      editorRef.current?.canvas.dispose()
      fabric.util.requestAnimFrame(() => {})
    }
  }, [])

  return {
    Canvas,
    ready: initialized
  }
}

export default useCanvas

Now, run react project... npm run start

Modify App.tsx or index.tsx, it crashes with the clearRect error.

Am I doing something wrong? Thank you for looking at this

subtext916 avatar Jul 23 '24 05:07 subtext916

Also, could it be related to this topic? https://github.com/fabricjs/fabric.js/issues/8299 (scroll down to the "React Compatibility" section)

subtext916 avatar Jul 23 '24 05:07 subtext916

Oh, one other detail. I am using: "fabric": "^5.3.0", "fabricjs-react": "^1.2.2",

subtext916 avatar Jul 25 '24 13:07 subtext916

not sure, can you prepare a codesanbox runtime, I think based on the sample code, there is no benefit for now to separate hook from canvas component, all together can be inside Canvas component (don't think returning component from hook looks like a good pattern)

asotog avatar Jul 31 '24 03:07 asotog