我正在创建一个织物画布和按钮,用于实例化应该可选择的形状。我不明白为什么我的组件在以下场景中被重新渲染两次。因此,我的织物形状无法选择。但是,当我从 index.tsx 文件中删除
<React.StrictMode>
时,渲染会发生一次,并且我的形状可供选择。我可以删除 <React.StrictMode>
但我不认为这是最好的解决方案。演示如下:
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/[email protected]/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>
为什么 useEffect 运行两次以及如何在 React 中很好地处理它? 有很好的答案描述了为什么会发生这种情况以及通用的解决方案。
在您的情况下,您需要清理实例化的画布。我对 Fabric 不熟悉,但是从阅读文档来看,
dispose
方法似乎很合适:
dispose() → {fabric.Canvas}
清除画布元素并删除所有事件侦听器
您需要从调用上述方法的
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/[email protected]/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>