[英]State is <empty string> when function is called on key event
In React, I have a number of buttons (imagine a PIN layout with numbers) that update the state on click.在 React 中,我有许多按钮(想象一个带有数字的 PIN 布局)在点击时更新状态。 I also added an event listener to the
document
so pressing keys on the keyboard updates the pin too.我还在
document
中添加了一个事件侦听器,因此键盘上的按键也会更新 pin。 However, there's a strange problem.但是,有一个奇怪的问题。 When I add a number by clicking a button, the state is working correctly and everything is fine, but when I press a key on a physical keyboard, the state updates, but logs as
<empty string>
!当我通过单击按钮添加数字时,状态正常工作并且一切正常,但是当我按下物理键盘上的键时,状态会更新,但会记录为
<empty string>
!
Here is the code:这是代码:
export default function Keypad() {
const [pin, setPin] = useState("");
function addNumber(num) {
console.log(pin); // returns the correct pin with handleKeyClick, returns <empty string> with handleKeyDown
if (pin.length < 6) { // only works if the pin is not <empty string>
setPin((pin) => [...pin, num.toString()]); // works correctly with both handleKeyClick and handleKeyDown even if pin logged <empty string>!
}
}
function handleKeyClick(num) {
addNumber(num);
}
function handleKeyDown(e) {
if (!isNaN(e.key)) {
addNumber(e.key);
}
}
useEffect(() => {
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, []);
return (
<div>
{/* just one button for example */}
<button onClick={() => handleKeyClick(9)}>9</button>
</div>
)
}
I guess this is because document
can't access the pin
state, but if it was the case, the setPin
shouldn't work either.我猜这是因为
document
无法访问pin
状态,但如果是这种情况, setPin
应该工作。 Am I right?我对吗?
Your component does not keep a reference when listening to DOM events, this answer has some neat code for listening to window events using a fairly simple hook.您的组件在侦听 DOM 事件时不保留引用,这个答案有一些简洁的代码,用于使用相当简单的钩子侦听窗口事件。 When applied to your code, it works as expected:
当应用于您的代码时,它按预期工作:
const {useState, useEffect, useRef} = React; // Hook function useEventListener(eventName, handler, element = window){ // Create a ref that stores handler const savedHandler = useRef(); // Update ref.current value if handler changes. // This allows our effect below to always get latest handler ... // ... without us needing to pass it in effect deps array ... // ... and potentially cause effect to re-run every render. useEffect(() => { savedHandler.current = handler; }, [handler]); useEffect( () => { // Make sure element supports addEventListener // On const isSupported = element && element.addEventListener; if (!isSupported) return; // Create event listener that calls handler function stored in ref const eventListener = event => savedHandler.current(event); // Add event listener element.addEventListener(eventName, eventListener); // Remove event listener on cleanup return () => { element.removeEventListener(eventName, eventListener); }; }, [eventName, element] // Re-run if eventName or element changes ); }; const Keypad = (props) => { const [pin, setPin] = useState([]); function addNumber(num) { console.log(pin); // returns the correct pin with handleKeyClick, returns <empty string> with handleKeyDown if (pin.length < 6) { // only works if the pin is not <empty string> setPin((pin) => [...pin, num.toString()]); // works correctly with both handleKeyClick and handleKeyDown even if pin logged <empty string>! } } function handleKeyClick(num) { addNumber(num); } function handleKeyDown(e) { if (!isNaN(e.key)) { addNumber(e.key); } } useEventListener("keydown", handleKeyDown) return ( <div> {/* just one button for example */} <button onClick={() => handleKeyClick(9)}>9</button> </div> ) return "Hello World" } ReactDOM.render(<Keypad />, document.getElementById("root"))
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.