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>
)
}