suchen

Heim  >  Fragen und Antworten  >  Hauptteil

Erneutes Rendern mit React.StrictMode und der Canvas-Instanz von fabric.js

Ich erstelle eine Stoffleinwand und Knöpfe. Die durch diese Knöpfe instanziierten Formen sollten auswählbar sein. Ich verstehe nicht, warum meine Komponente in der folgenden Situation zweimal neu gerendert wird. Daher kann meine Stoffform nicht ausgewählt werden. Wenn ich jedoch 时,渲染只发生一次,我的形状是可选择的。我可以移除 aus meiner index.tsx-Datei entferne, halte ich das nicht für die beste Lösung. Hier ist die Demo:

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粉659378577431 Tage vor852

Antworte allen(1)Ich werde antworten

  • 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>

    Antwort
    0
  • StornierenAntwort