未使用CRA的React项目中useWindowSize Hook resize时出错求助
我编写了一个用于返回窗口宽高的useWindowSize Hook,在CRA(Create React App)创建的项目中运行正常,但在自行搭建的Webpack+React项目中出现异常:初始渲染能正常获取窗口尺寸,但调整窗口大小时抛出错误Uncaught TypeError: can't access property "type", currentEvent is null。怀疑问题与Webpack配置或依赖版本有关,相关代码如下:
useWindowSize Hook代码
import { useState, useEffect } from "react"; export default function useWindowSize() { const [windowSize, setWindowSize] = useState({ width: undefined, height: undefined, }); useEffect(() => { const handleResize = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }); window.addEventListener("resize", handleResize); handleResize(); return () => window.removeEventListener("resize", handleResize); }, []); return windowSize; }
webpack.config.js
const path = require("path"); const HTMLWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.jsx", output: { path: path.join(__dirname, "/dist"), filename: "bundle.js", }, plugins: [ new HTMLWebpackPlugin({ template: "./public/index.html", }), ], module: { rules: [ { test: /\.jsx|js$/, include: path.resolve(__dirname, "src"), exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: [ [ "@babel/preset-env", { targets: "defaults", }, ], "@babel/preset-react", ], }, }, }, { test: /\.(jpe?g|png|ico|svg|gif)$/i, use: { loader: "file-loader", options: { name: "[name].[ext]" }, }, }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, ], }, };
package.json
{ "name": "cra", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --open --hot", "build": "webpack --mode production" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "css-loader": "^6.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", "style-loader": "^3.3.1" }, "devDependencies": { "@babel/core": "^7.20.2", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "babel-loader": "^9.1.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.11.1" } }
解决方案
这个错误是React 18开发模式下Strict Mode与Webpack热更新(HMR)的组合问题:Strict Mode会双重执行副作用,事件监听的清理时序与状态更新冲突,叠加HMR的触发逻辑导致currentEvent为null的异常。
可以通过以下几种方式快速解决:
用requestAnimationFrame包裹状态更新
避免resize事件频繁触发导致的状态更新冲突:const handleResize = () => { requestAnimationFrame(() => { setWindowSize({ width: window.innerWidth, height: window.innerHeight }); }); };禁用React Strict Mode
若不需要Strict Mode的开发检查,可在入口文件中移除<StrictMode>包裹:// 修改前 import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; createRoot(document.getElementById('root')).render( <StrictMode> <App /> </StrictMode> ); // 修改后 createRoot(document.getElementById('root')).render(<App />);调整Babel React预设配置
确保React 18的自动导入逻辑正确,在@babel/preset-react中添加runtime: 'automatic':// webpack.config.js中babel-loader的options presets: [ ['@babel/preset-env', { targets: "defaults" }], ['@babel/preset-react', { runtime: 'automatic' }] ]关闭Webpack热更新
在webpack.config.js的devServer配置中禁用HMR:module.exports = { // ...其他配置 devServer: { hot: false, open: true } }
其中,禁用Strict Mode或用requestAnimationFrame包裹状态更新是最直接的解决方式。
内容的提问来源于stack exchange,提问作者paveilleux

