我正在使用Next.js构建一个简单的计算器应用程序。当用户使用HTML按钮时,一切都像预期的那样工作,但我也希望他能够使用他的键盘,所以我设置了一个事件侦听器,但是由于某种原因,当用户使用指定的按钮时,数字状态重置为0,然后更改为指定的数字(即使数字应该连接到现有的数字)。
代码如下:
'use client'
import styles from './page.module.css'
import React, {useState, useEffect, useRef} from "react";
import Image from "next/image";
export default function Home() {const [number, setNumber] = useState('0');
const [number2, setNumber2] = useState('');
const [lastOperator, setLastOperator] = useState(null);
const [clear, setClear] = useState('AC');
const [isOperational, setIsOperational] = useState(false);
const numberRef = useRef();
const number2Ref = useRef();
useEffect(() => {
document.addEventListener("keydown", handleKeyPress);
// Clean up the event listener when the component unmounts.
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [])
function handleKeyPress(event) {
console.log('number: ' + number)
const keyMap = {
'1': () => appendNumber('1'),
'2': () => appendNumber('2'),
'3': () => appendNumber('3'),
'4': () => appendNumber('4'),
'5': () => appendNumber('5'),
'6': () => appendNumber('6'),
'7': () => appendNumber('7'),
'8': () => appendNumber('8'),
'9': () => appendNumber('9'),
'0': () => appendNumber('0'),
'Enter': () => pressEquals(),
};
const key = event.key;
if (key in keyMap) {
event.preventDefault(); // Prevent the default browser behavior for the key.
keyMap[key]();
}
}
function adjustFontSize() {
const numberElement = numberRef.current;
const numberWidth = numberElement.scrollWidth;
const containerWidth = numberElement.parentElement.scrollWidth;
if (numberWidth > (containerWidth) - containerWidth * 0.08) {
const newFontSize = (containerWidth * 0.92 / numberWidth) * parseFloat(getComputedStyle(numberElement).fontSize);
numberElement.style.fontSize = `${newFontSize}px`;
if (isOperational) {
number2Ref.current.style.fontSize = `${newFontSize}px`;
}
} else {
numberElement.style.fontSize = '5rem';
if (isOperational) {
number2Ref.current.style.fontSize = '3rem';
}
}
}
function appendNumber(digit) {
if (number !== '0') {
if (isOperational) {
setNumber2(number);
setNumber(digit);
} else {
setNumber(number + digit);
}
} else {
setNumber(digit);
setClear('C');
}
adjustFontSize(numberRef);
if (isOperational) {
setIsOperational(false);
}
}
function pressC() {
setNumber('0');
setNumber2('0');
setIsOperational(false);
setLastOperator(null);
setClear('AC');
adjustFontSize();
}
function pressNeg() {
if (number.startsWith('-')) {
setNumber(number.slice(1));
} else if (number !== '0') {
setNumber('-' + number);
}
adjustFontSize();
}
function pressPoint() {
if (!number.includes('.')) {
setNumber(number + '.');
}
adjustFontSize();
}
function pressOperator(operator) {
setLastOperator(operator);
if (number2 !== '0') {
pressEquals();
setIsOperational(true);
} else {
setNumber2(number);
setNumber('0');
}
}
function pressBackspace() {
setNumber(number.slice(0, number.length - 1))
adjustFontSize();
}
function pressEquals() {
const isFloatingPoint = number.includes('.') || number2.includes('.');
const num1 = isFloatingPoint ? parseFloat(number) : parseInt(number);
const num2 = isFloatingPoint ? parseFloat(number2) : parseInt(number2);
switch (lastOperator) {
case '+':
setNumber((num2 + num1).toString());
break;
case '-':
setNumber((num2 - num1).toString());
break;
case '*':
setNumber((num2 * num1).toString());
break;
case '/':
setNumber((num2 / num1).toString());
break;
case '^':
setNumber(Math.pow(num2, num1).toString());
break;
}
setNumber2('0');
}
return (
<main className={styles.main}>
<div id={styles.calculator}>
<div id={styles.display}>
<div id={styles.number2Div}>
<p id={styles.number2} ref={number2Ref}>{number2}</p>
</div>
<div id={styles.numberDiv}>
<p id={styles.number} ref={numberRef}>{number}</p>
</div>
</div>
<div id={styles.buttons}>
<button onClick={pressC} className={styles.button}>{clear}</button>
<button onClick={pressNeg} className={styles.button}>neg</button>
<button onClick={() => pressOperator('^')} className={styles.button}>^</button>
<button onClick={() => pressOperator('/')} className={styles.button}>/</button>
<button onClick={() => appendNumber('1')} className={styles.button}>1</button>
<button onClick={() => appendNumber('2')} className={styles.button}>2</button>
<button onClick={() => appendNumber('3')} className={styles.button}>3</button>
<button onClick={() => pressOperator('*')} className={styles.button}>*</button>
<button onClick={() => appendNumber('4')} className={styles.button}>4</button>
<button onClick={() => appendNumber('5')} className={styles.button}>5</button>
<button onClick={() => appendNumber('6')} className={styles.button}>6</button>
<button onClick={() => pressOperator('-')} className={styles.button}>-</button>
<button onClick={() => appendNumber('7')} className={styles.button}>7</button>
<button onClick={() => appendNumber('8')} className={styles.button}>8</button>
<button onClick={() => appendNumber('9')} className={styles.button}>9</button>
<button onClick={() => pressOperator('+')} className={styles.button}>+</button>
<button onClick={pressBackspace} className={styles.button}><Image width={64} height={64}
src="/backspace.png" alt="Back"/>
</button>
<button onClick={() => appendNumber('0')} className={styles.button}>0</button>
<button onClick={pressPoint} className={styles.button}>.</button>
<button onClick={pressEquals} className={styles.button}>=</button>
</div>
</div>
</main>
)
}
我真的找不到原因,因为其他国家保持不变。我试图查看当用户按下按钮时调用的appendNumber函数,但我找不到任何错误,因为当用户按下HTML按钮时,它可以正常工作。我还设置了一些日志记录,发现在到达该函数之前,数字已经为0,这让我更加困惑
我还尝试启用React的严格模式,但这并没有改变什么
1条答案
按热度按时间voj3qocg1#
handleKeyPress
作为事件处理程序,在number
和它引用的其他状态上有一个陈旧的闭包。稍微更新一下代码,将
handleKeyPress
(declared each render cycle)存储在React ref中,该React ref由用作事件侦听器的 stable 回调引用引用。这是为了访问handleKeyPress
的 current“version”,其中 current 闭包覆盖状态。范例:
您还需要舍入可以按下使用计算器的其余键。举例来说: