React中获取id为matrix的canvas元素返回null的问题求助
问题描述
我想给React组件内的canvas添加脚本实现矩阵特效,但执行脚本时,document.getElementById("matrix")返回null,无法获取目标canvas元素。
已尝试的方案
- 参考方案使用
useEffect - 将脚本引入移至body标签末尾
- 替换
document.getElementById("matrix")为document.querySelector("#matrix")
相关代码
index.html 的body部分
<body> <div id="root" class="has-navbar-fixed-top"></div> <script async scr="../src/MatrixEffect/matrix.js"></script> </body>
matrix.js 脚本代码
window.onload = Init; function Init() { const canvas = document.getElementById("matrix"); /* const canvas = document.querySelector("#matrix"); */ console.log(canvas); /* 返回null */ const context = canvas.getContext("2d"); }
MatrixComponent.js 代码
import styles from "./Matrix.module.css"; const MatrixComponent = () => { return ( <div className={`${styles.container}`}> <canvas id="matrix" className={`${styles.canvas}`}></canvas> </div> ); }; export default MatrixComponent;
报错信息
matrix.js:7 Uncaught TypeError: Cannot read properties of null (reading 'getContext')
at Init (matrix.js:7:1)
问题原因
核心问题是外部脚本的执行时机早于React组件渲染出canvas元素:
window.onload仅等待HTML文档和静态资源加载完成,但React是在#root容器内动态渲染组件,window.onload触发时,MatrixComponent还未完成渲染,canvas元素不存在。- 脚本标签的
async属性会让脚本异步加载执行,进一步加大了脚本先于组件渲染的概率。 - 额外注意:脚本标签里的
scr是拼写错误,正确应为src,这会导致脚本加载失败(若你实际执行了脚本,大概率是笔误)。
解决办法
方案1:将矩阵逻辑移入React组件(推荐,符合React最佳实践)
直接在MatrixComponent中使用useEffect和useRef,确保canvas渲染完成后再执行逻辑:
import { useEffect, useRef } from "react"; import styles from "./Matrix.module.css"; const MatrixComponent = () => { // 用useRef获取canvas元素 const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; // 判空避免报错 if (!canvas) return; const context = canvas.getContext("2d"); // 在这里编写你的矩阵特效逻辑,比如初始化、帧绘制等 console.log("canvas已就绪", canvas); // 可选:组件卸载时清理资源,比如取消动画帧 return () => { // cancelAnimationFrame(animationId); }; }, []); // 空依赖数组确保只执行一次 return ( <div className={`${styles.container}`}> <canvas ref={canvasRef} className={`${styles.canvas}`}></canvas> </div> ); }; export default MatrixComponent;
这种方式完全贴合React生命周期,代码模块化,无需依赖外部脚本。
方案2:调整外部脚本执行时机(不推荐,非React标准写法)
若坚持使用外部脚本,需确保脚本在React组件渲染完成后执行:
- 修正脚本标签的拼写错误:将
scr改为src,并移除async属性
<body> <div id="root" class="has-navbar-fixed-top"></div> <script src="../src/MatrixEffect/matrix.js"></script> </body>
- 修改
matrix.js,移除window.onload绑定,将Init暴露到全局:
function Init() { const canvas = document.getElementById("matrix"); if (!canvas) { console.error("未找到canvas元素"); return; } const context = canvas.getContext("2d"); // 编写特效逻辑 } // 暴露到全局,让React入口文件调用 window.Init = Init;
- 在React入口文件(如
index.js)中,根组件渲染完成后手动调用Init:
import React from 'react'; import ReactDOM from 'react-dom/client'; import MatrixComponent from './MatrixEffect/MatrixComponent'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <MatrixComponent /> </React.StrictMode> ); // 根组件渲染完成后执行矩阵初始化 if (window.Init) { window.Init(); }
内容的提问来源于stack exchange,提问作者Ewerton Xavier
相关产品推荐
相关产品推荐

