React中handleKeyUp函数无法访问更新后的状态变量求助
Hey there! Great first post—no worries if you're still getting the hang of things, we’ve all been there. Let’s break down why your handleKeyUp function is only seeing the initial versions of correctLetters and wrongLetters, and how to fix it.
The Root Problem
Your useEffect that adds the keyup listener has an empty dependency array ([]), which means it only runs once when the component first mounts. At that moment, the handleKeyUp function "captures" the initial state values of correctLetters, wrongLetters, and word. Even when those states update later, the event listener is still using that original version of handleKeyUp—so it can’t access the fresh state data.
The Solution
We need to make sure the event listener gets updated whenever the state or the handleKeyUp function changes. Here’s the step-by-step fix:
- Memoize
handleKeyUpwithuseCallback
This ensures the function only recreated when its dependencies (the states it relies on) change, instead of on every render. - Update the
useEffectdependencies
AddhandleKeyUpto the dependency array so the effect re-runs (removes the old listener and adds the new one) whenever the function updates. - Add a cleanup function
This prevents memory leaks by removing the event listener when the component unmounts or when the effect re-runs.
Modified Code
const WORDS = ['application', 'programming', 'interface', 'wizard'] const selectedWord = WORDS[Math.floor(Math.random() * WORDS.length)] function App() { const [word, setWord] = useState(selectedWord) const [correctLetters, setCorrectLetters] = useState([]) const [wrongLetters, setWrongLetters] = useState([]) const [showNotification, setShowNotification] = useState(false) // Wrap handleKeyUp with useCallback to memoize it const handleKeyUp = useCallback((e) => { if (e.keyCode >= 65 && e.keyCode <= 90) { const letter = e.key if (word.includes(letter)) { if (!correctLetters.includes(letter)) { setCorrectLetters(letters => [...letters, letter]) } else { handleNotification() } } else { if (!wrongLetters.includes(letter)) { setWrongLetters(letters => [...letters, letter]) } else { handleNotification() } } } }, [word, correctLetters, wrongLetters]) // Add all dependencies here // Update useEffect with cleanup and correct dependencies useEffect(() => { document.addEventListener("keyup", handleKeyUp) // Cleanup: remove listener when component unmounts or effect re-runs return () => { document.removeEventListener("keyup", handleKeyUp) } }, [handleKeyUp]) // Now depend on the memoized handleKeyUp // Don't forget to define the handleNotification function! const handleNotification = () => { setShowNotification(true) // Optional: Auto-hide notification after 2 seconds setTimeout(() => setShowNotification(false), 2000) } // Rest of your component logic/rendering here... }
Quick Bonus Note
You were already using the functional update form (setCorrectLetters(letters => [...letters, letter])) which is awesome—it ensures you’re always working with the most up-to-date state when making updates. The main issue was just the stale closure from the event listener, which the useCallback and updated useEffect fix perfectly.
内容的提问来源于stack exchange,提问作者Sdrummolo

