Next.js计算器应用程序:键盘输入重置数字状态

nwsw7zdq  于 2023-10-18  发布在  其他
关注(0)|答案(1)|浏览(95)

我正在使用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的严格模式,但这并没有改变什么

voj3qocg

voj3qocg1#

handleKeyPress作为事件处理程序,在number和它引用的其他状态上有一个陈旧的闭包。
稍微更新一下代码,将handleKeyPressdeclared each render cycle)存储在React ref中,该React ref由用作事件侦听器的 stable 回调引用引用。这是为了访问handleKeyPresscurrent“version”,其中 current 闭包覆盖状态。
范例:

const handleKeyPressRef = useRef(handleKeyPress);
handleKeyPressRef.current = handleKeyPress;

useEffect(() => {
  const handler = (e) => handleKeyPressRef.current(e);

  document.addEventListener("keydown", handler);

  // Clean up the event listener when the component unmounts.
  return () => {
    document.removeEventListener("keydown", handler);
  };
}, []);

您还需要舍入可以按下使用计算器的其余键。举例来说:

function handleKeyPress(event) {
  const keyMap = {
    "1": appendNumber.bind(null, "1"),
    "2": appendNumber.bind(null, "2"),
    "3": appendNumber.bind(null, "3"),
    "4": appendNumber.bind(null, "4"),
    "5": appendNumber.bind(null, "5"),
    "6": appendNumber.bind(null, "6"),
    "7": appendNumber.bind(null, "7"),
    "8": appendNumber.bind(null, "8"),
    "9": appendNumber.bind(null, "9"),
    "0": appendNumber.bind(null, "0"),
    "/": pressOperator.bind(null, "/"),
    "*": pressOperator.bind(null, "*"),
    "-": pressOperator.bind(null, "-"),
    "+": pressOperator.bind(null, "+"),
    "^": pressOperator.bind(null, "^"),
    ".": pressPoint,
    Enter: pressEquals,
    Backspace: pressBackspace
  };

  const key = event.key;

  if (key in keyMap) {
    event.preventDefault(); // Prevent the default browser behavior for the key.
    keyMap[key]();
  }
}

相关问题