Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to use this in a hook? #44

Open
subtext916 opened this issue Jul 22, 2024 · 6 comments
Open

Is it possible to use this in a hook? #44

subtext916 opened this issue Jul 22, 2024 · 6 comments

Comments

@subtext916
Copy link

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
Copy link
Author

subtext916 commented Jul 22, 2024

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

@asotog
Copy link
Owner

asotog commented Jul 22, 2024

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

@subtext916
Copy link
Author

subtext916 commented Jul 23, 2024

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
Copy link
Author

subtext916 commented Jul 23, 2024

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

@subtext916
Copy link
Author

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

@asotog
Copy link
Owner

asotog commented Jul 31, 2024

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants