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

React组件重复渲染问题:按键触发后console.log输出次数指数级增长

问题原因分析

你遇到的指数级渲染增长问题,核心原因是每次组件渲染都会重复给document添加新的keydown事件监听,且没有清理旧的监听:

  • 组件初始渲染时添加第一个监听;按下方向键触发setLeft,组件重新渲染,又添加第二个监听;
  • 再次按键时,两个监听都会执行,每个都调用setLeft,导致组件两次渲染,又新增两个监听;
  • 以此类推,监听数量每次翻倍,触发更新的次数也指数级增长,console.log('plane')的输出次数自然跟着翻倍。

另外还有两个小问题:

  • useState初始化时未设置默认值,第一次渲染left为undefined,会导致style里的left变成undefined%;
  • 监听里直接使用left值,会因为闭包问题拿到旧的状态值,可能导致移动逻辑出错。
修复代码
import React from "react";
import { useState, useEffect } from "react";

export default function Plane() {
  // 给left设置初始默认值,避免第一次渲染的undefined问题
  const [left, setLeft] = useState(50);

  useEffect(() => {
    const handleKeyDown = function (e) {
      switch (e.keyCode) {
        // 使用函数式更新,确保拿到最新的left状态
        case 37:
          return setLeft(prevLeft => prevLeft - 1);
        case 39:
          return setLeft(prevLeft => prevLeft + 1);
        case 38:
          return console.log("up");
        case 40:
          return console.log("down");
      }
    };

    // 只在组件挂载时添加一次监听
    document.addEventListener("keydown", handleKeyDown);

    // 组件卸载时移除监听,避免内存泄漏和重复监听
    return () => {
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, []); // 空依赖数组,确保useEffect只执行一次

  console.log('plane')
  return <div className="playerControl" style={{ left: `${left}%` }}></div>;
}
关键修复点
  • 将keydown监听逻辑放到useEffect中,并配合清理函数移除监听,确保整个组件生命周期内只有一个监听生效;
  • 使用setLeft的函数式更新形式prevLeft => prevLeft ±1,避免闭包导致的旧状态值问题;
  • 给useState设置初始值50,去掉原来多余的useEffect初始化逻辑,简化代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 08:50:26