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
相关产品推荐
相关产品推荐

