React中useEffect执行两次致Canvas重复绘制问题求助
React useEffect执行两次导致Canvas文字重复绘制问题解决
问题描述
我正在开发一个基于HTML Canvas的白板应用,在Canvas上绘制文字时发现内容被重复绘制了两次。调试后发现挂载时的useEffect钩子执行了两次,但我期望它仅在组件挂载时执行一次。相关代码如下:
import React, { useEffect, useRef } from "react"; const WhiteBoard = () => { const canvasid = useRef(); useEffect(() => { const canvas = canvasid.current; const ctx = canvas.getContext("2d"); ctx.font = "30px Impact"; ctx.rotate(0.1); ctx.fillText("Awesome!", 50, 100); // Draw line under text var text = ctx.measureText("Awesome!"); ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.beginPath(); ctx.lineTo(50, 102); ctx.lineTo(50 + text.width, 102); ctx.stroke(); }, []); return ( <> <div>WhiteBoard</div> <canvas ref={canvasid} style={{ border: "1px solid red" }}></canvas> </> ); }; export default WhiteBoard;
原因分析
这是React 18在开发环境下的Strict Mode默认行为。Strict Mode会刻意重复执行不带依赖项的useEffect等生命周期钩子,目的是帮你提前检测潜在的副作用问题(比如未正确清理的资源、重复创建的订阅),生产环境下不会出现这个重复执行的情况。
解决方法
1. 开发环境临时禁用Strict Mode(可选)
如果只是想在开发阶段避免这个现象,可以在根组件中移除<StrictMode>包裹。比如index.js中:
// 移除<StrictMode>标签 ReactDOM.createRoot(document.getElementById('root')).render( <App /> );
但不推荐长期这么做,因为Strict Mode能帮你发现很多隐藏的代码问题。
2. 给Canvas添加清理逻辑(推荐)
更规范的做法是在useEffect执行绘制前先清空Canvas,确保每次绘制都是在干净的画布上进行。同时可以在清理函数中做卸载时的清理:
useEffect(() => { const canvas = canvasid.current; const ctx = canvas.getContext("2d"); // 绘制前清空整个Canvas ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.font = "30px Impact"; ctx.rotate(0.1); ctx.fillText("Awesome!", 50, 100); // 绘制文字下划线 const text = ctx.measureText("Awesome!"); ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.beginPath(); ctx.lineTo(50, 102); ctx.lineTo(50 + text.width, 102); ctx.stroke(); // 组件卸载时清空Canvas(可选) return () => { ctx.clearRect(0, 0, canvas.width, canvas.height); }; }, []);
另外,建议给Canvas设置固定的width和height属性,避免绘图缓冲区尺寸与显示尺寸不一致的问题:
<canvas ref={canvasid} width={800} height={600} style={{ border: "1px solid red" }}></canvas>
内容的提问来源于stack exchange,提问作者Yash Narang
相关产品推荐
相关产品推荐

