Fabric.js in React Context API

Viewed 35

Im creating a web app whereby a user drags and drops shapes onto a canvas. I am using fabric.js and React.

I'm trying to use fabric within the context api to make the canvas available across the app. I have found this repo which implements what I want and works correctly when I run their example.

I am using typescript but keep getting an error when I try to use the canvas from the context within my app.

I think the error is something to do with the useState but cant work out what is happening. Any help would be appreciated.

Here is my code:

// FabricContext.tsx
import React, { createContext, useCallback, useContext, useState } from "react";
import { fabric } from 'fabric'

interface IFabricContext {
  canvas: fabric.Canvas | null
  initCanvas: any
}

export const FabricContext = createContext<IFabricContext>({} as IFabricContext)

export const FabricContextProvider = ({ children }: { children: React.ReactNode }) => {
  const [canvas, setCanvas] = useState<fabric.Canvas | null>(null)

  const initCanvas = useCallback((el: HTMLCanvasElement, options: fabric.ICanvasOptions) => {
    const canvasOptions: fabric.ICanvasOptions = {
      ...options
    }
    let c = new fabric.Canvas(el, canvasOptions)
    c.renderAll()
    setCanvas(c)
  }, [])

  return (
    <FabricContext.Provider
      value={{ canvas, initCanvas }}
    >
      {children}
    </FabricContext.Provider>
  )
}

export const useFabricContext = (): IFabricContext => useContext(FabricContext) 
// Canvas.tsx
import { useLayoutEffect, useRef } from "react"
import { useFabricContext } from "../../context/FabricContext"

export default function Canvas(): JSX.Element {
  const canvasRef = useRef(null)
  const { initCanvas } = useFabricContext()

  useLayoutEffect(() => {
    initCanvas(canvasRef.current, {
      width: 500,
      height: 500
    })
  }, [canvasRef, initCanvas])

  return (
    <canvas ref={canvasRef} style={{ border: "1px solid red" }} />
  )
}
// App.tsx
import { fabric } from "fabric";
import Canvas from "./components/Canvas";
import { FabricContextProvider, useFabricContext } from "./context/FabricContext";

export default function AgilityCourseDesigner() {
  const { canvas } = useFabricContext()

  // Here is where I get the error
  // Uncaught TypeError: Cannot read properties of undefined (reading 'add')
  canvas!.add(new fabric.Circle({ top: 140, left: 230, radius: 75, fill: 'green' }))

  return (
    <FabricContextProvider>
      <Canvas />
    </FabricContextProvider>
  )
}
0 Answers
Related