You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署fabric.js编辑器时点击添加图形出现Cannot read properties of null错误

问题原因

你遇到的线上报错Uncaught TypeError: Cannot read properties of null (reading 'add'),核心原因是FabricCanvas组件中fabric canvas的初始化时机错误:

在FabricCanvas的useLayoutEffect里,你把initCanvas(new fabric.Canvas('c'))写在了effect的清理函数(return的函数)中,这意味着这段代码只会在组件卸载时执行,而非组件挂载时。本地开发环境可能因热重载、组件重复渲染等偶然情况触发组件卸载再挂载,导致canvas被初始化;但线上生产环境组件通常只挂载一次,canvas始终为null,点击按钮调用canvas.add自然会报错。

修复方案

1. 修正FabricCanvas的canvas初始化时机

将canvas初始化逻辑从清理函数移到effect主函数中,同时添加组件卸载时的资源清理逻辑:

// FabricCanvas.js
import React, { useContext, useLayoutEffect } from 'react';
import { fabric } from 'fabric';
import ContextCanvas from '../../context/ContextCanvas';

const FabricCanvas = () => {
  const [, initCanvas] = useContext(ContextCanvas);

  useLayoutEffect(() => {
    // 组件挂载时初始化fabric canvas
    const fabricCanvas = new fabric.Canvas('c');
    initCanvas(fabricCanvas);

    // 组件卸载时销毁canvas,避免内存泄漏
    return () => {
      fabricCanvas.dispose();
      initCanvas(null);
    };
  }, [initCanvas]); // 添加依赖,避免闭包问题

  return (
    <>
      <canvas
        id="c"
        width={window.innerWidth}
        height={window.innerHeight}
      />
    </>
  )
}
export default FabricCanvas;

2. 给ContextCanvas添加空值防护

在Context的initCanvas方法中添加空值判断,避免传入null时调用renderAll:

// ContextCanvas.js
import { createContext, useState } from 'react';
import { fabric } from 'fabric';

const ContextCanvas = createContext();

export function CanvasProvider({ children }) {
  const [canvas, setCanvas] = useState(null);
  const initCanvas = c => {
    setCanvas(c);
    // 仅当canvas实例存在时调用renderAll
    if (c) c.renderAll();
  };

  return (
    <ContextCanvas.Provider value={[canvas, initCanvas]}>
      {children}
    </ContextCanvas.Provider>
  );
}

export default ContextCanvas;

3. 在添加元素的方法中增加空值检查

在FabricTextBox的addTextBox方法里先判断canvas是否存在,防止未初始化时触发错误:

// FabricTextBox.js
import { fabric } from 'fabric';
import { useContext } from 'react';
import ContextCanvas from '../../../context/ContextCanvas';
import { Button } from '@chakra-ui/react';

const FabricTextBox = () => {
  const [canvas] = useContext(ContextCanvas);
  
  function addTextBox() {
    // 先检查canvas是否初始化完成
    if (!canvas) return;
    
    const textbox = new fabric.Textbox('Click on the Rectangle to move it.', {
      fontSize: 20,
      left: 50,
      top: 100,
      width: 200,
      fill: 'black',
      color: 'white',
      cornerColor: 'blue',
    });
    canvas.add(textbox);
    canvas.requestRenderAll();
  }
  
  return (
    <>
      <Button
        type="button"
        colorScheme="blue"
        onClick={addTextBox}
        variant={'ghost'}
        _hover={{}}
        _focus={{}}
        _active={{}}
        textColor={'white'}
        fontWeight={'light'}
      >
        Text Field
      </Button>
    </>
  );
};

export default FabricTextBox;

额外注意

确保你的应用根组件已经用CanvasProvider包裹,所有需要访问canvas的组件(比如FabricCanvas、FabricTextBox)都在Provider的作用域内。

内容的提问来源于stack exchange,提问作者Sohaib Ashraf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 04:36:17