You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中window.addEventListener绑定点击事件导致浏览器冻结问题

浏览器冻结的根本原因

你的代码触发了指数级增长的重渲染死循环,逻辑链路是:

  • React函数组件每次重渲染时,整个函数体内的代码都会从头执行一遍。你把window.addEventListener直接写在了组件函数顶层,意味着组件每渲染一次,就会给window新增一个click事件监听,从来不会移除旧的监听。
  • 首次挂载时,window上绑定了第一个click监听,点击触发setState→组件重渲染→新增第二个click监听。
  • 下一次点击时,两个监听同时触发,连续执行两次setState→触发两轮重渲染→新增两个click监听,此时window上一共堆了4个监听。
  • 监听数量、重渲染次数会随着点击指数级暴涨,几轮下来就会占满浏览器主线程,直接导致页面冻结。
  • 额外隐患:你直接拿闭包里捕获的window_touched变量计算新值,当多个监听同时触发时,很容易拿到过期的旧值,计数也会不准。
修复方案

需要做两处核心调整:

  1. 把事件绑定逻辑移到useEffect里,传入空依赖数组,保证事件只在组件首次挂载时绑定一次,同时在effect的返回值里写卸载逻辑,组件销毁时移除对应监听,避免内存泄漏。
  2. 当新state依赖旧state值时,使用函数式更新写法,保证每次计算都能拿到最新的state值,不会受闭包旧值影响。

修复后的完整代码:

import "./App.css";
import { useState, useEffect } from "react";

function App() {
  // 修正了原代码里的拼写错误:thouched -> touched,改用React社区通用的小驼峰命名
  const [windowTouched, setWindowTouched] = useState(0);

  useEffect(() => {
    const handleWindowClick = () => {
      // 函数式更新,始终拿到前一次的最新state值
      setWindowTouched(prevCount => prevCount + 1);
    };
    // 挂载时绑定一次监听
    window.addEventListener("click", handleWindowClick);
    // 卸载时清理监听
    return () => {
      window.removeEventListener("click", handleWindowClick);
    };
  }, []); // 空依赖代表effect仅在挂载、卸载时执行

  return (
    <div className="App">
      {`This window has been touched ${windowTouched} times`}
    </div>
  );
}

export default App;

内容的提问来源于stack exchange,提问作者lemme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:06:26