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

React Strict Mode下Fabric Canvas重复渲染及图形选中异常问题

问题:React严格模式下Fabric Canvas添加的图形无法选中

开启<React.StrictMode>时,组件重复渲染两次,Fabric Canvas被初始化两次,导致添加的图形无法选中;关闭严格模式后组件仅渲染一次,图形可正常选中。不想直接关闭严格模式,寻求解决方案。

原因分析

React 18的严格模式在开发环境中会重复执行副作用函数(如useEffect),用于检测未清理的副作用和潜在bug。你的代码中,每次useEffect执行都会创建新的Fabric Canvas实例并覆盖canvasRef.current,但第一个实例已经绑定了DOM元素,第二个实例再次绑定同一DOM会造成实例冲突:后续添加的图形实际被加到旧实例中,而交互事件由新实例接管,因此无法选中图形。

解决方案

1. 避免重复初始化Canvas

在初始化逻辑中先检查canvasRef.current是否已存在,若存在则直接返回,不重复创建实例。

2. 添加副作用清理函数

在useEffect中添加清理逻辑,销毁Canvas实例,避免内存泄漏,同时解决严格模式下重复初始化的冲突问题。

修改后的代码

JavaScript代码

const { Fragment, StrictMode, useEffect, useRef } = React;
const { createRoot } = ReactDOM;

const styles = {};

const CanvasComponent = ({ id }) => {
    const canvasRef = useRef(null);

    useEffect(() => {
        console.log('init canvas');
        canvasRef.current = initCanvas();
        
        // 清理函数:销毁Canvas实例
        return () => {
            if (canvasRef.current) {
                canvasRef.current.dispose();
                canvasRef.current = null;
            }
        };
    }, []);

    const initCanvas = () => {
        // 避免重复创建实例
        if (canvasRef.current) {
            return canvasRef.current;
        }
        return new fabric.Canvas(`canvas-${id}`, {
            width: 800,
            height: 400,
        });
    };

    const addShape = (shapeType) => {
        let shape;
        switch (shapeType) {
            case 'circle':
                shape = new fabric.Circle({ radius: 30, fill: 'red', left: 100, top: 100 });
                break;
            case 'rectangle':
                shape = new fabric.Rect({ width: 60, height: 70, fill: 'green', left: 100, top: 100 });
                break;
            default:
                return;
        }
        canvasRef.current.add(shape);
    };

    return (
        <div>
            <button onClick={() => addShape('circle')}>Add Circle</button>
            <button onClick={() => addShape('rectangle')}>Add Rectangle</button>
            <div className={styles.canvasContainer}>
                <canvas id={`canvas-${id}`}></canvas>
            </div>
        </div>
    );
}

function StrictModeEnabled() {
    return <StrictMode><h1>Strict Mode Enabled</h1><CanvasComponent id={1} /></StrictMode>;
}

function StrictModeDisabled() {
    return <Fragment><h1>Strict Mode Disabled</h1><CanvasComponent id={2} /></Fragment>;
}

const strictModeEnabledRoot = createRoot(document.getElementById("strict-mode-enabled"));
strictModeEnabledRoot.render(<StrictModeEnabled />);

const strictModeDisabledRoot = createRoot(document.getElementById("strict-mode-disabled"));
strictModeDisabledRoot.render(<StrictModeDisabled />);

HTML代码

<script crossorigin src="https://www.unpkg.com/fabric@5.3.0/dist/fabric.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="strict-mode-enabled"></div>
<div id="strict-mode-disabled"></div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 09:16:01