JavaScript Calculator Project - Front End Libraries Projects - Help Needed

I have merged the two threads.


Right now the handleEqualsButtonClick function will get invoked no matter what you click on (even just clicking the document) and it has no logic for checking if its code should run or not, i.e. it is not checking the event target before running its code (so wasEqualsClicked will be set to true no matter where you click).

This is one of the main downsides of just attaching listeners to the document, as opposed to passing each element its appropriate event handler. If you have more then one or two listeners attached to the document it quickly can become tedious (and a bit messy) to have to check the event target before running the function code.

For the most, when possible, I would also suggest checking that the target is not the expected target and returning out of the function instead of checking that it is the expected target. That way the bulk of the function code does not end up inside a bunch of conditions.

Thanks for the tip.

So should I attach the event listener to the Keypad component so that it’s on the buttons in general (is this possible?), and then write code to have the even handler code return immediately if it’s not the expected event target that was clicked before writing code to handle the expected target being clicked?

Also, how can I change the layout of the buttons to one where the memory buttons are laid out in the same row and the rest are laid out 4 buttons per row?

Edit: Is it okay to do Keypad.addEventListener (for example)? Or is that a bad idea?

You can still take the approach you are taking now. I was just making it clear that it can have its pros and cons.

But yes, you can attach event listeners to elements instead of the document. You can also just add an onClick to each element with the appropriate handler. So for example all the number buttons would have an onClick={handleNumberButtonClick}. How you want to accomplish this depends.

Looping one array and constructing all the buttons is data-driven and has both pros and cons. It might feel like a win to just have React loop one data structure and spit out a bunch of buttons but you may also lose some control in the process. Again, it too can become so generic that it may make it more difficult to pass down props to the correct elements (or just harder to reason about). Some times writing more JSX by hand will give you more direct control over the elements (like more targeted control over what props you pass down).

I might suggest searching for some examples of React calculators and looking at some different approaches that can be taken. See if you can find bits and pieces to learn from and use an approach you like (or a combination of different approaches). It may also give you an idea about what type of code you find is easier to read and reason about. Sure it is just you coding this, but it is always a good idea to code as if someone else was going to read your code (or even work on it).

Is it not possible to assign an event listener to the Keypad component itself? If it is, I’ll just do that and check for what button was clicked. If it’s not possible, then I’ll render individual buttons separately in the Keypad component and pass onClick handlers to them.

Well technically the Keypad component isn’t an element, it is just a bunch of buttons. You can wrap it inside an element that can have an event listener.

Makes sense.

I just checked and for now I need to successfully append numbers I click after clicking an operator to the #stored paragraph element. Right now it just gets appended to the string in the #current string which I don’t want. When I click the equals button the current value is appended to the stored value which is what I want, but of course I get an error at that moment from expr-eval, so I asked on their GitHub repository about that.

Here’s the App component:

const App = props => {
  const [currentValue, setCurrentValue] = useState("0");
  // const [currentMemoryValue, setMemory] = useState("");
  const [storedValue, setStoredValue] = useState("");

  let wasOperatorClicked = false;
  let wasEqualsClicked = false;

  const handleNumberButtonClick = event => {
    if (!wasOperatorClicked || !wasEqualsClicked) {
      if (event.target.name === "number-button") {
        if (currentValue === "0") {
          setCurrentValue(event.target.textContent);
        } else {
          setCurrentValue(currentValue.concat(event.target.textContent));
        }
      }
    } else if (wasEqualsClicked) {
      if (event.target.name === "number-button") {
        setCurrentValue(event.target.textContent);
      }
    } else if (wasOperatorClicked) {
      setStoredValue(storedValue.concat(event.target.textContent));
      setCurrentValue(event.target.textContent);
    }
  };

  const handleOperatorButtonClick = event => {
    if (!(event.target.name === "add" || event.target.name === "minus" ||
    event.target.name === "multipy" || event.target.name === "divide")) {
      return null;
    } else {
      wasOperatorClicked = true;
      if (event.target.name === "add" || event.target.name === "minus" ||
          event.target.name === "multipy" || event.target.name === "divide") {
        setStoredValue(`${currentValue} ${event.target.textContent} `);
      }
    }
  };

  // calculate the result for the current
  // value and display it, then reset the
  // stored value to empty string
  const parser = new exprEval.Parser();
  const handleEqualsButtonClick = event => {
    if (!(event.target.textContent === "=")) {
      return null;
    } else {
      wasEqualsClicked = true;
      if (event.target.textContent === "=") {
        setStoredValue(storedValue.concat(currentValue));
        setCurrentValue(parser.parse(storedValue).evaluate());
        setStoredValue("");
      }
    }
  };

  useEffect(() => {
    document.addEventListener("click", handleEqualsButtonClick);
    document.addEventListener("click", handleNumberButtonClick);
    document.addEventListener("click", handleOperatorButtonClick);

    return () => {
      document.removeEventListener("click", handleNumberButtonClick);
      document.removeEventListener("click", handleEqualsButtonClick);
      document.removeEventListener("click", handleOperatorButtonClick);
    };
  });

  return (
    <React.Fragment>
      <Display
        storedValue={storedValue}
        currentValue={currentValue}
      />
      {buttons.map((object, index) =>
        <Keypad
          key={index}
          className={object.className}
          id={object.id}
          name={object.name}
          value={object.value}
        />
      )}
    </React.Fragment>
  );
};

Do I need to change where I’m setting the event listeners right now or can I make this work as is? Either way, please help me out here. What am I doing wrong?

Also, should the Booleans for checking if an operator or the equals button was clicked be in state or should it be a regular variable?

I’m going to try and implement the memory functions after everything else is done. That’s why the state stuff for it is commented out right now.

Right, I believe the wasOperatorClicked and wasEqualsClicked are getting redeclared on re-render. Try making them state variables instead.


Edit: I should also have mentioned that all your event handlers will fire on any document click. Having them all attached to the document is really not necessary. You can have one generic click handler that checks the click target and delegates the function calls.

const handleSomething = () => {
  console.log("do something");
};

const handleSomethingElse = () => {
  console.log("do somethingElse");
};

const clickHandler = (event) => {
  if (event.target.id === "something") handleSomething();
  if (event.target.id === "somethingElse") handleSomethingElse();
};

document.addEventListener("click", clickHandler);

I tried making clicks on the document; nothing happened. I think it’s fine with the if checks I’ve added. Unless I missed something.

But yeah, adding them to the document is too broad. I want to add them to the buttons themselves and check what button was clicked in the handlers. Do I have to render each button by hand, without a loop, and use onClick to set the handlers if I want to go that route?

Is there something wrong here?

  const handleNumberClick = event => {
    if (!operatorButtonClicked && !equalsButtonClicked) {
      if (currentValue === "0") {
        setCurrentValue(event.target.textContent);
      }
      const newValue = currentValue.concat(event.target.textContent);
      setCurrentValue(newValue);
    } else if (!operatorButtonClicked && equalsButtonClicked) {
      if (currentValue === "0") {
        setCurrentValue(event.target.textContent);
      }
      setCurrentValue(event.target.textContent);
      setStoredValue("");
    } else if (operatorButtonClicked && !equalsButtonClicked) {
      if (currentValue === "0") {
        setCurrentValue(event.target.textContent);
      }
      setCurrentValue(event.target.textContent);
    }
  };

  const parser = new exprEval.Parser();
  const handleEqualsClick = event => {
    setEqualsClicked(true);
    const stored = storedValue.concat(currentValue);
    setCurrentValue(parser.parse(storedValue).evaluate());
    setStoredValue(`${stored} =`);
  };

  const handleOperatorClick = event => {
    setOperatorClicked(true);
    const stored = storedValue.concat(`${currentValue} ${event.target.textContent}`);
    setStoredValue(stored);
  };

  const handleClearClick = () => {
    setStoredValue("");
    setCurrentValue("0");
  };

  const handleClearEntryClick = () => {
    setCurrentValue("0");
  };

  const clickHandler = event => {
    if (event.target.name === "number-button") {
      handleNumberClick(event);
    } else if (event.target.name === "add" || event.target.name === "minus" ||
    event.target.name === "mulitply" || event.target.name === "divide") {
      handleOperatorClick(event);
    } else if (event.target.name === "clear") {
      handleClearClick();
    } else if (event.target.name === "clear-entry") {
      handleClearEntryClick();
    } else if (event.target.name === "equals") {
      handleEqualsClick(event);
    } else {
      return null;
    }
  };

  useEffect(() => {
    document.addEventListener("click", clickHandler);

    return () => {
      document.removeEventListener("click", clickHandler);
    };
  });
  1. When I click on a number while the current value is 0, the number gets appended to the string so that if the number clicked was 9, it becomes “09”.
  2. When I run the tests from FCC, I get the same TEOF: EOF error 7 times plus 12 or so other collapsed stack frames. The TEOF: EOF error is from these lines of code:
217 |  const handleEqualsClick = event => {
  218 |    setEqualsClicked(true);
  219 |    const stored = storedValue.concat(currentValue);
> 220 |    setCurrentValue(parser.parse(storedValue).evaluate());
      | ^  221 |    setStoredValue(`${stored} =`);
  222 |  };
  223 | 

and

247 | } else if (event.target.name === "clear-entry") {
  248 |   handleClearEntryClick();
  249 | } else if (event.target.name === "equals") {
> 250 |   handleEqualsClick(event);
      | ^  251 | } else {
  252 |   return null;
  253 | }

This happens after you see how many tests passed and how many failed.

The C and CE buttons work.

I changed the equals click handler to this:

const parser = new exprEval.Parser();
  const handleEqualsClick = event => {
    setEqualsClicked(true);
    const stored = `${storedValue}${currentValue}`;
    setStoredValue(stored);
    console.log(storedValue);
    let calculatedValue;
    try {
      calculatedValue = parser.parse(storedValue).evaluate();
    } catch (err) {
      console.log(`Error occurred: ${err}`);
    }
    setCurrentValue(`${calculatedValue}`);
    setStoredValue(`${stored}${event.target.textContent}`);
  };

and made sure to remove spaces from the string constructed as the value for storedValue. But for some reason, the value logged to the console in handleEqualsClick is just ${number}${operator}. The number that should be right after that isn’t logged with the rest of that. Is that what’s going into the parse function? Or is it just not able to log the whole thing before the error is thrown?

And for some reason calculatedValue is undefined.

What am I doing wrong here? Could someone please help me out here?

@lasjorg My code on GitHub is updated. I have a weird problem where numbers after an operator aren’t appended to the value of the #stored paragraph element. I try to do that in the handleEqualsClick function:

  const parser = new exprEval.Parser();
  const handleEqualsClick = event => {
    setEqualsClicked(true);
    const stored = `${storedValue}${currentValue}`;
    setStoredValue(stored);
    console.log(currentValue);
    if (storedValue.match(/[0-9]$/) === null) {
      setStoredValue(`${storedValue}${currentValue}`);
      console.log(storedValue);
    }
    console.log(storedValue);
    const calculatedValue = parser.parse(storedValue).evaluate();
    setCurrentValue(`${calculatedValue}`);
    setStoredValue(`${stored}${event.target.textContent}`);
  };

Can you tell from that what the issue could be? And I get an “unexpected TEOF: EOF” error from the line where I’m using parser.parse(). I don’t understand what that error is or how to fix it.

I finally got the equals button to work correctly.

But I still have that problem where after I click “Run Tests” in the JCC tests suite container and get the results, I get this error message:

Error: expected variable for assignment
▶ 3 stack frames were collapsed.
handleEqualsClick
E:/programming/react/javascript-calculator/src/js/components/App.js:215
  212 | const handleEqualsClick = event => {
  213 |   setEqualsClicked(true);
  214 |   const stored = `${storedValue}${currentValue}`;
> 215 |   const calculatedValue = parser.parse(stored).evaluate();
      | ^  216 |   setCurrentValue(`${calculatedValue}`);
  217 |   setStoredValue(stored.concat(event.target.textContent));
  218 | };
View compiled
HTMLButtonElement.clickHandler
E:/programming/react/javascript-calculator/src/js/components/App.js:286
  283 |     } else if (event.target.name === "clear-entry") {
  284 |       handleClearEntryClick();
  285 |     } else if (event.target.name === "equals") {
> 286 |       handleEqualsClick(event);
      | ^  287 |     }
  288 |   }
  289 | };
View compiled

I have this code in the main click handler function:

 const clickHandler = event => {
    if (event.target.classList.contains("keypad-button")) {
      if (event.target.name === "number-button") {
        handleNumberClick(event);
      } else if (event.target.name === "add" || event.target.name === "minus" ||
      event.target.name === "multiply" || event.target.name === "divide") {
        handleOperatorClick(event);
      } else if (event.target.name === "clear") {
        handleClearClick();
      } else if (event.target.name === "clear-entry") {
        handleClearEntryClick();
      } else if (event.target.name === "equals") {
        handleEqualsClick(event);
      }
    } else {
      return null;
    }
  };

And I’ve applied the click event handler to all buttons like this:

useEffect(() => {
    const buttons = document.getElementsByTagName("button");
    for (const button of buttons) {
      button.addEventListener("click", clickHandler);
    }

    return () => {
      for (const button of buttons) {
        button.removeEventListener("click", clickHandler);
      }
    };
  });

I’ve added a class “keypad-button” all of the buttons, too. So what’s missing? How can I make it so the event handlers only run when a button on my keypad is clicked?

Edit: I put the whole keypad inside a div with id="keypad" and attached a click event listener to the div. Code in the handleClick function is the same as above. But I still have that problem where I get an error after running the tests from FCC.

Okay.

7 of the tests currently pass. One of the ones that don’t is about the “clear” button clearing out the input and setting the display to show “0”. Mine does that, but the test doesn’t pass.

Here’s the updated code on GitHub. Could someone please help me out here? What did I do wrong?

1 Like

Latest update:

My code on GitHub is updated. Here’s the repository link again: https://github.com/DragonOsman/javascript-calculator

My question: For the test about the display being cleared and showing only 0 when the button with id of “clear” is clicked on the FCC tests suite for the project, it says it expected “\n\n\n0 to equal 0” and I don’t know why mine is “\n\n\n0” at all; could someone help me out here?

For the test that says:

As I input numbers, I should be able to see my input in the element with the id of "display"

Mine fails with the error “Numbers do not display correctly within id=“display” : expected ‘\n\n\n3’ to equal ‘123’”. Even though when I click the numbers, I see them appear on the display as they should.

I have a div with with id of “display” with two nested paragraph elements, one with id of “stored” and the other with the id of “current” (the former is above the latter). When the user clicks on a number, it appears in the #current element, and the formula being evaluated appears in the #stored element. Numbers from the #current element are appended to the #stored element when an operator is clicked, with the clicked operator appearing after the number.

I have 7 out of 16 tests currently passing.

Related to that, I also have an issue where stored is undefined on line 274 even though I’ve defined it earlier in the code. In this function:

  const handleOperatorClick = event => {
    setOperatorClicked(true);
    let stored;

    // Have to set "/" and "*" characters for multiplication
    // and division because with event.target.textContent values,
    // expr-eval parser will error
    // And if stored value is not an empty string, append the current value
    // and the operator to it; otherwise, just set it to the current value
    // with the operator next to it.
    if (event.target.textContent === "+") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}${event.target.textContent}`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}${event.target.textContent}`;
      }
    } else if (event.target.textContent === "÷") {
      if (storedValue.length > 0) {
        stored = `${currentValue}${currentValue}/`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}/`;
      }
    } else if (event.target.textContent === "×") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}*`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}*`;
      }
    } else if (event.target.textContent === "-") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}${event.target.textContent}`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}${event.target.textContent}`;
      }
    }

    if (reciprocalClicked) {
      stored = `${storedValue}${event.target.textContent}`;
    }

    const prevStoredValue = stored.slice(0, stored.length - 1);
    if (math.parse(prevStoredValue) !== null) {
      const calculatedValue = math.evaluate(prevStoredValue);
      setCurrentValue(`${calculatedValue}`);
    }

    setStoredValue(stored);
  };

It’s undefined on the line where I’m doing

const prevStoredValue = stored.slice(0, stored.length - 1);

Any help is appreciated. Thanks in advance. (And sorry for the multiple consecutive posts).

Edit: I managed to fix the problem with stored becoming undefined there, but I also have a problem where after an operator or equals sign is clicked, all numbers clicked replace the one before it in the currentValue string instead of getting appended to it, so I need to check if numbers were consecutively clicked after an operator or equals button was clicked. How do I do that?

I’ve added an array called input to state, with a function setInput, using useState but I keep getting an error input.push is not a function when I try to push something to it. Full updated code: https://github.com/DragonOsman/javascript-calculator . What am I doing wrong?

I added that array in to more easily see what input was last and handle number input based on that, like if the last number entered was a 0 and there was no other number entered before that, then any nonzero number will replace the 0, otherwise the number is to be appended to the existing value. But I’m having trouble with this; the test in the FCC tests suite that I have for this isn’t passing (it tries to input “123” but only manages to enter “13” or something, though when I try I get “123”. But I’ve also noticed that when I try to enter a number immediately clicking on an operator, it appends the number to the existing value instead of starting a new value).

So yeah, again, what am I doing wrong? How do I fix these issues? Any help is appreciated.

Updated code is in repository now, here.

I’m having a hard time figuring how to get the app to now allow numbers starting with multiple zeroes. I have the test in “Technology Stack” and tests 1 through 8 in the “Tests” section in the tests suite passing.

I need help in getting the rest of them to pass.

Edit: I got test #10 in “Tests”, 10. When inputting numbers, my calculator should not allow a number to begin with multiple zeros. to pass. But I still have that problem where when I try to click a number, then an operator and then a number, the new number clicked doesn’t replace the one in the display already like I want it to. What am I doing wrong?

Hi, everyone.

I think it’s better if I just open a new thread about this again now. It can be merged with the other one once the purpose is done with.

Anyway. My updated calculator source code is on the GitHub repository now.

I’m having trouble getting any tests aside from the 1 through 8 and 10 in “Tests” to pass. I’ve tried everything I can think of for tests 8, 11 and 14.

So yeah: What am I doing wrong or missing here? Any help is appreciated. Thanks.

Edit: Okay, so it got merged after all. Sorry for making a new thread about it then I guess.

But I really do need help here. I can’t figure out what I’m doing wrong on my own. Even just a little hint would be great at this point. I may sound like I’m begging here, but I can’t really help it can I?

It’s really a LOT of code to review, but I get that it must be a little frustrating to talk to the void …

One thing that came to mind, when I scrolled over your code - and I’m really just throwing an idea at you that might or might not work - is that you use an isNumeric method from a library, and it seems to me you’re using it on a value that you got from the DOM using input.textContent (along those lines). Is it possible you’re not paying attention to types and that the numbers you’re trying to handle are actually strings?

The calculator is taking input as strings. When I use isNumeric in the number click handler I’m trying to check if the last button clicked was a number button. I want it to append the number to the value on the display if the current value isn’t still “0” and if the last button clicked was a number button, and in all other cases I want it to replace the value that’s there with the new one.

But with what I’ve tried so far, it’s not doing it correctly.

I’ll post the whole App component code here:

const App = props => {
  const [currentValue, setCurrentValue] = useState("0");
  const [storedValue, setStoredValue] = useState("");
  const [reciprocalClicked, setReciprocalClicked] = useState(false);
  const [input, setInput] = useState([]);
  const [lastClicked, setLastClicked] = useState("");
  const operators = ["+", "-", "÷", "×"];

  const config = {
    epsilon: 1e-12,
    matrix: "Matrix",
    number: "number",
    precision: 64,
    predictable: false,
    randomSeed: null
  };

  const math = create(all, config);
  const handleNumberClick = event => {
    if (input.length > 0) {
      if (input.length === 1) {
        setLastClicked(input[0]);
      } else if (input.length > 1) {
        setLastClicked(input[input.length - 1]);
      }
    }
    const newInput = input;
    newInput.push(event.target.textContent);
    setInput(newInput);

    if (!isNaN(lastClicked) && currentValue !== "0") {
      setCurrentValue(currentValue.concat(event.target.textContent));
    } else if (operators.includes(lastClicked) || lastClicked === "=" ||
        lastClicked === "1/𝑥" || currentValue === "0") {
      setCurrentValue(event.target.textContent);
    }
  };

  const handleEqualsClick = event => {
    if (event.target.tagName === "BUTTON" &&
    event.target.classList.contains("keypad-button") &&
    event.target.classList.contains("calculation-submit")) {
      const newInput = input;
      newInput.push(event.target.textContent);
      setInput(newInput);

      const stored = `${storedValue}${currentValue}`;
      try {
        const calculatedValue = math.evaluate(stored);
        setCurrentValue(`${calculatedValue}`);
      } catch (err) {
        console.log(`${err}`);
      }
      setStoredValue(stored.concat(event.target.textContent));

      // remove all elements from input array except this '=' click
      newInput.slice(-1);
      setInput(newInput);
    }
  };

  const handleOperatorClick = event => {
    let stored;

    if (input.length > 0) {
      if (input.length === 1) {
        setLastClicked(input[0]);
      } else if (input.length > 1) {
        setLastClicked(input[input.length - 1]);
      }
    }
    const newInput = input;

    // if last button clicked was another operator, have
    // the newly clicked one replace it unless it's '-'
    // because the next number clicked may have to become
    // a negative number
    if (operators.includes(lastClicked) && lastClicked !== "-") {
      newInput.slice(0, newInput.length - 1);
    }
    newInput.push(event.target.textContent);
    setInput(newInput);
    console.log(lastClicked);

    // Have to set "/" and "*" characters for multiplication
    // and division because with event.target.textContent values,
    // math parser library will error
    // And if stored value is not an empty string, append the current value
    // and the operator to it; otherwise, just set it to the current value
    // with the operator next to it.
    if (event.target.textContent === "+") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}${event.target.textContent}`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}${event.target.textContent}`;
      }
    } else if (event.target.textContent === "÷") {
      if (storedValue.length > 0) {
        stored = `${currentValue}${currentValue}/`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}/`;
      }
    } else if (event.target.textContent === "×") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}*`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}*`;
      }
    } else if (event.target.textContent === "-") {
      if (storedValue.length > 0) {
        stored = `${storedValue}${currentValue}${event.target.textContent}`;
      } else if (storedValue.length === 0) {
        stored = `${currentValue}${event.target.textContent}`;
      }
    }

    if (reciprocalClicked) {
      stored = `${storedValue}${event.target.textContent}`;
    }

    setStoredValue(stored);

    console.log(input);
  };

  const handleClearClick = () => {
    setStoredValue("");
    setCurrentValue("0");
    setInput([]);
  };

  const handleClearEntryClick = () => {
    setCurrentValue("0");
  };

  const handleReciprocalClick = event => {
    setReciprocalClicked(true);
    if (input.length > 0) {
      if (input.length === 1) {
        setLastClicked(input[0]);
      } else if (input.length > 1) {
        setLastClicked(input[input.length - 1]);
      }
    }
    const newInput = input;
    newInput.push(event.target.textContent);
    setInput(newInput);

    if (lastClicked === "=") {
      setStoredValue(`(1/${currentValue})`);
      const calculatedValue = math.evaluate(storedValue);
      setCurrentValue(`${calculatedValue}`);
    }

    if (storedValue.length === 0) {
      const calculationStr = `1/${currentValue}`;
      const calculatedValue = math.evaluate(calculationStr);
      setCurrentValue(`${calculatedValue}`);
      setStoredValue(`(1/${currentValue})`);
    } else if (storedValue.length > 0) {
      const calculatedStr = `${storedValue}(1/${currentValue})`;
      const calculatedValue = math.evaluate(calculatedStr);
      setCurrentValue(`${calculatedValue}`);
      setStoredValue(`${storedValue}(1${currentValue})`);
    }
  };

  const handleDecimalClick = event => {
    if (input.length > 0) {
      if (input.length === 1) {
        setLastClicked(input[0]);
      } else if (input.length > 1) {
        setLastClicked(input[input.length - 1]);
      }
    }
    const newInput = input;
    newInput.push(event.target.textContent);
    setInput(newInput);

    if (!currentValue.includes(event.target.textContent) && lastClicked !== event.target.textContent) {
      setCurrentValue(currentValue.concat(event.target.textContent));
    }
  };

  const clickHandler = event => {
    if (event.target.classList.contains("keypad-button")) {
      if (event.target.name === "number-button") {
        handleNumberClick(event);
      } else if (event.target.name === "add" || event.target.name === "minus" ||
      event.target.name === "multiply" || event.target.name === "divide") {
        handleOperatorClick(event);
      } else if (event.target.name === "clear") {
        handleClearClick();
      } else if (event.target.name === "clear-entry") {
        handleClearEntryClick();
      } else if (event.target.name === "equals") {
        handleEqualsClick(event);
      } else if (event.target.name === "reciprocal-function") {
        handleReciprocalClick(event);
      } else if (event.target.name === "decimal") {
        handleDecimalClick(event);
      }
    } else {
      return null;
    }
  };

  useEffect(() => {
    const keypadDiv = document.getElementById("keypad");
    keypadDiv.addEventListener("click", clickHandler);

    return () => {
      keypadDiv.removeEventListener("click", clickHandler);
    };
  });

  return (
    <React.Fragment>
      <Display
        storedValue={storedValue}
        currentValue={currentValue}
      />
      <div id="keypad">
        {buttons.map((object, index) =>
          <Keypad
            key={index}
            className={object.className}
            id={object.id}
            name={object.name}
            value={object.value}
          />
        )}
      </div>
    </React.Fragment>
  );
};

With this, when I click on a number after clicking on an operator, it appends the new value to the one in the display instead of setting the current value to the new one. I want it to reset the current value with new one in that case. How do I do this?

If you think I should post the full code, let me know.

I decided to just use isNaN to check if a string is a number. mathjs's isNumeric isn’t working.

I tried logging lastClicked. It seems t’s not really being set to the textContent of the last button clicked. Why? Could someone please help me out here?

This is what I have now in the number click handler code:

  const handleNumberClick = event => {
    if (input.length > 0) {
      if (input.length === 1) {
        setLastClicked(input[0]);
      } else if (input.length > 1) {
        setLastClicked(input[input.length - 1]);
      }
    }
    const newInput = input;
    newInput.push(event.target.textContent);
    setInput(newInput);

    if (!isNaN(lastClicked) && currentValue !== "0") {
      setCurrentValue(currentValue.concat(event.target.textContent));
    }

    if (operators.includes(lastClicked)) {
      console.log(`lastClicked is ${lastClicked}`);
      setCurrentValue(event.target.textContent);
    }

    if (lastClicked === "=") {
      setStoredValue("");
      setCurrentValue(event.target.textContent);
    }

    if (lastClicked === "1/𝑥") {
      setCurrentValue(event.target.textContent);
    }

    if (currentValue === "0") {
      setCurrentValue(event.target.textContent);
    }
  };