首页  >  问答  >  正文

使用React.StrictMode和fabric.js的Canvas实例进行重渲染

我正在创建一个织物画布和按钮,这些按钮实例化的形状应该是可选择的。我不明白为什么在以下情况下我的组件会被重新渲染两次。因此,我的织物形状无法选择。然而,当我从我的index.tsx文件中移除时,渲染只发生一次,我的形状是可选择的。我可以移除,但我不认为这是最好的解决方案。下面是演示:

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

const styles = {};

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

    useEffect(() => {
        console.log('init canvas'); // displayed twice with <React.StrictMode>
        canvasRef.current = initCanvas();
    }, []);

    const initCanvas = () => (
        canvasRef.current = new fabric.Canvas(`canvas-${id}`, {
            width: 800,
            height: 400,
        })
    );

    const addShape = (shapeType: string) => {
        let shape: fabric.Object;
        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 />);
<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>

P粉659378577P粉659378577418 天前843

全部回复(1)我来回复

  • P粉850680329

    P粉8506803292023-09-22 18:33:02

    问题

    为什么 useEffect 在 React 中运行两次,如何处理? 这个问题有很好的答案,描述了为什么会发生这种情况以及一个通用的解决方案。

    解决方案

    在你的情况下,你需要清理实例化的画布。我对 Fabric 不熟悉,但从阅读文档中,dispose 方法 似乎是合适的:

    你需要从 useEffect 返回一个调用上述方法的函数。从链接的问题中可以看出,从 useEffect 返回一个进行清理的函数是一个好的实践。下面也有一个工作示例:

    const { Fragment, StrictMode, useEffect, useRef } = React;
    const { createRoot } = ReactDOM;
    
    const styles = {};
    
    const CanvasComponent = ({ id }) => {
        const canvasRef = useRef(null);
    
        useEffect(() => {
            console.log('init canvas'); // displayed twice with <React.StrictMode>
            canvasRef.current = initCanvas();
            
             return () => canvasRef.current.dispose();
        }, []);
    
        const initCanvas = () => (
            canvasRef.current = new fabric.Canvas(`canvas-${id}`, {
                width: 800,
                height: 400,
            })
        );
    
        const addShape = (shapeType: string) => {
            let shape: fabric.Object;
            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>;
    }
    
    const strictModeEnabledRoot = createRoot(document.getElementById("strict-mode-enabled"));
    strictModeEnabledRoot.render(<StrictModeEnabled />);
    <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>

    回复
    0
  • 取消回复