Why can't I save a number with decimal values in my input box? It only allows integers to be updated?

Viewed 16

I have an text input box on my form, and when the form initially displays it displays the price with a decimal point like 910.01.

const initialState: OrderWindowState = {        
    price1: 910.01,
    price2: 911.33,
    
  }


const [state, setState] = useState<OrderWindowState>(initialState);



<input type="text" name="price1" value={state.price1} onChange={price1OnChange} />


const price1OnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    event.preventDefault();
    console.log("price1OnChange called:" + event.target.value);    
    setState({
      ...state,
      price1: +event.target.value
    })
  };

When I begin to update the number, if I enter a decimal point . I can see it in my console.log message, but the number stored in my state and visibly in my text input it only displays integer values (with no decimal values).

Why is it doing this?

1 Answers

When you do +event.target.value, the string value is converted into a number - and numbers don't have trailing .s. So, when the component re-renders, the previously typed . won't be reflected in state, so it won't be seen in the controlled component's value either.

One approach would be to keep the string input in state, and to only convert it to a number later when needed. For example:

const App = () => {
    const [price1, setPrice1] = React.useState(910.01);
    const price1OnChange = (event) => {
        setPrice1(event.target.value);
    };
    const price1Number = Number(price1);
    return (
      <div>
        <input type="text" name="price1" value={price1} onChange={price1OnChange} />
        <div>
          Number value:
          {Number.isNaN(price1Number) ? 'Not a number - correct your input' : price1Number}
        </div>
      </div>
    );
};

ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>

The above is in JS just so that the snippet can be run easily, but the same approach works for TypeScript - change your OrderWindowState to accept strings instead of numbers, and change price1: +event.target.value to price1: event.target.value.

Because the values for price1 and price2 are separate, you might consider using separate states for them (like I did above and as React recommends).

Related