按下Enter键修改状态触发React Too Many re-renders错误如何解决?
问题根因
你遇到的无限重渲染问题本质是闭包陷阱 + useEffect依赖配置错误,具体逻辑如下:
- 你在useEffect中绑定了键盘监听事件,但是依赖数组只填了
index,onKeyPress函数内部用到的timerState没有被纳入依赖 - 这就导致你绑定的
onKeyPress永远持有首次渲染时的闭包,里面的timerState永远是初始值false - 每次你按下Enter键,都会命中
if(timerState === false)的判断,调用setTimerState(true),触发组件重渲染 - 重渲染后新的
onKeyPress函数里的timerState已经是true,但因为useEffect的依赖没有变化,不会重新绑定事件,旧的监听函数还在,后续每次按Enter都会重复触发setTimerState,最终触发React的重渲染次数限制报错。
修复方案
你可以按以下步骤修改代码:
- 先引入
useCallbackHook - 用
useCallback包裹onKeyPress函数,把它用到的所有外部依赖(timerState、index、words)都加到useCallback的依赖数组里 - 把包裹后的
onKeyPress加到useEffect的依赖数组里,保证每次onKeyPress变化时都会重新绑定最新的事件监听 - 可选优化:判断按键匹配时加边界判断,避免index超出words数组长度时报错
修改后的核心代码示例:
// 新增引入useCallback import React, { useState, useEffect, useCallback } from 'react'; function App() { const [index, setIndex] = useState(0); const [showSignIn, setShowSignIn] = useState(false); const [timerState, setTimerState] = useState(false); // 用useCallback包裹onKeyPress,声明完整依赖 const onKeyPress = useCallback((event) => { console.log("Current key: ", event.key); switch (event.key) { case "Enter": if(timerState === false){ setTimerState(true); } break; case "Backspace": break; default: // 新增数组越界判断 if(index < words.length && event.key === words[index]){ setIndex((prev) => prev + 1); } break; } }, [timerState, index, words]) const openSignIn = () => { setShowSignIn(prev => !prev); }; // 替换useEffect的依赖为onKeyPress useEffect(() => { document.addEventListener('keydown', onKeyPress); return () => { document.removeEventListener('keydown', onKeyPress); }; }, [onKeyPress]) // 其余return代码保持不变 }
额外说明
如果你的项目开启了ESLint的React Hooks检查规则,它会自动提示你缺失的依赖项,能帮你避免这类闭包相关的问题。
内容的提问来源于stack exchange,提问作者Colin Harker
相关产品推荐
相关产品推荐

