Hernan Ariel
Hernan Ariel

Reputation: 273

Limiting number of inputs

I´m trying to make a React calculator. It´s mostly done, but I have one problem I don´t know how to correct: I don´t want to let users enter expressions starting with more than one "0" AND expressions such as "01" should be replaced to just "1". I thought about using string methods to limit that, but it´s not budging. EDIT: I know why my solution doesn´t work (input will never have more than one zero), but I don´t know any other way to fix it.

class Calculator extends Component {
constructor(props) {
    super(props);
    this.state = { value: "" };
    this.handleClick = this.handleClick.bind(this);
}
handleClick(evt) {
    const id = evt.target.id;
    const result = evt.target.value;
    if (id === "clear") {
    this.setState({ value: 0 });
    } else if (id === "equals") {
    this.setState({ value: math.eval(this.state.value) });
    } else {
    result.toString().includes("00");
    this.setState({ value: this.state.value + result.replace("00", "0") });
    console.log(this.state.value);
    }
}

render() {
    return (
    <div id="container">
        <Display value={this.state.value} />
        <Button onClick={this.handleClick} id="zero" value={"0"} />
        <Button onClick={this.handleClick} id="one" value={"1"} />
        <Button onClick={this.handleClick} id="two" value={"2"} />
        <Button onClick={this.handleClick} id="three" value={"3"} />
        <Button onClick={this.handleClick} id="four" value={"4"} />
        <Button onClick={this.handleClick} id="five" value={"5"} />
        <Button onClick={this.handleClick} id="six" value={"6"} />
        <Button onClick={this.handleClick} id="seven" value={"7"} />
        <Button onClick={this.handleClick} id="eight" value={"8"} />
        <Button onClick={this.handleClick} id="nine" value={"9"} />
        <Button onClick={this.handleClick} id="decimal" value={"."} />
        <Button onClick={this.handleClick} id="equals" value={"="} />
        <Button onClick={this.handleClick} id="clear" value={"clear"} />
        <Button onClick={this.handleClick} id="add" value={"+"} />
        <Button onClick={this.handleClick} id="subtract" value={"-"} />
        <Button onClick={this.handleClick} id="multiply" value={"*"} />
        <Button onClick={this.handleClick} id="divide" value={"/"} />
    </div>
    );
}
}
export default Calculator;

Upvotes: 1

Views: 157

Answers (3)

Ryan H.
Ryan H.

Reputation: 7844

A few pointers...

  • If you want value to be a string, make sure you don't change it to a number anywhere. You're doing this when id === 'clear'
  • If you only want to test for "00" at the beginning of value, use startsWith, not includes.
  • If you are using setState and the new state depends on the previous state, you must first access the state's previous value. The rationale for this is in the docs.

@daniel-hilgarth provides the correct way to use setState in this case.

Remove leading zero:

this.setState(prevState => ({
  value: `${prevState.value}${result}`.replace(/^0+\B/, "")
}));

There's all sorts of ways to do this. The regex above will identify leading zeros. For the case where you have only "0", it doesn't match at all because "0" followed by a end of string (or boundary) doesn't match the regex which is "0" followed by "not a word boundary".

Upvotes: 2

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174329

result will always contain just a single digit. What you want is to add the new digit to the existing value and only then replace:

this.setState(prevState => ({ value: (prevState.value + result).replace("00", "0") }));

However, this code is buggy, because you would never be able to enter 100, it would always be changed to 10.

So the correct logic would be to add a start anchor:

this.setState(prevState => ({ value: (prevState.value + result).replace(/^00/, "0") }));

UPDATE:

To achieve your actual goal, you can use something like the following (not the cleanest code, but should get my point across):

import React from "react";

const availableButtons = [
  "0",
  "1",
  "2",
  "3",
  "4",
  "5",
  "6",
  "7",
  "8",
  "9",
  ".",
  "=",
  "clear"
];
const operators = ["+", "-", "*", "/"];

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.state = { values: [], value: "", currentNumber: "" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick(value) {
    if (value === "clear") {
      this.setState({ values: [], value: "", currentNumber: "" });
    } else if (value === "=") {
      this.setState({
        value: eval(this.state.value),
        values: [],
        currentNumber: ""
      });
    } else {
      if (operators.indexOf(value) !== -1) {
        this.setState(prevState => ({
          values: [...prevState.values, prevState.currentNumber, value],
          currentNumber: "",
          value: prevState.value + value
        }));
      } else {
        this.setState(prevState => {
          const currentNumber = this.formattedNumber(
            prevState.currentNumber,
            value
          );
          const newValue = prevState.values.join("") + currentNumber;
          return {
            currentNumber,
            value: newValue
          };
        });
      }
    }
  }

  formattedNumber(previousNumber, newSymbol) {
    if (!previousNumber && previousNumber !== 0) return newSymbol;
    if (
      previousNumber.length === 1 &&
      previousNumber === "0" &&
      newSymbol !== "."
    ) {
      return newSymbol;
    } else {
      return previousNumber + newSymbol;
    }
  }

  render() {
    return (
      <div id="container">
        {this.state.value}
        <br />
        {availableButtons.concat(operators).map(x => (
          <button onClick={() => this.handleClick(x)} key={x}>
            {x}
          </button>
        ))}
      </div>
    );
  }
}
export default Calculator;

Working sample

Upvotes: 2

pritesh
pritesh

Reputation: 2192

You can change this line

this.setState({ value: this.state.value + result.replace("00", "0") });

to this

let num = this.state.value + Number(result);
this.setState({ value: Number(num).toString()});

I guess that will solve.

Upvotes: 0

Related Questions