Building a currency input with React and TypeScript
Tags: react, typescript, webdev, tutorial
Published: 2020-07-16 22:44:11

---
title: Building a currency input with React and TypeScript
published: true
description: How to build an input field that shows a formatted string
tags: react, typescript, webdev, tutorial
//cover_image: https://direct_url_to_image.jpg
---

> Hi guys, this is my first post here –and first blog ever. Any feedback is welcome!

A while ago I built a component library for React in TypeScript with styled-components. This library included an input element for large amounts of money, for example `500000`. For the sake of readability and convenience, formatting the input like `500.000` was a no-brainer.

Easy, right? Just use an `<input type="text" />`, apply some formatting by adding JavaScript sprinkles or add a library that does it for you. But this causes a problem. The initial value for the input element comes from an API and is a number, but a text field would convert it into a string. Now we could just parse the string value to a number before passing the property to the parent component and do a request to the server. However, with this approach the component wouldn't work out-of-the-box, since you would need extra logic outside the component to make it work, –which would also neglect the principles of a design system. So how do we handle everything inside this currency component?

> *I didn't include any design whatsoever, but here you'll find an [example](https://mobile.twitter.com/ambrookhuis/status/1129794928299708418)*.

Let’s start with the interface and definition of our `CurrencyInput`.

```jsx
interface Props {
  value: number | undefined;
  updateValue: (value: number | undefined) => void;
}

const CurrencyInput: React.FC<Props> = ({
  value: valueFromProp,
  updateValue,
}) => {
	return (
    <input
      type="text"
      value={currentValue}
      onChange={handleChange}
    />
  )
}
```

Notice that I renamed the `value` property to `valueFromProp`, since we need the name `value` later on. The value of the input element is set to `currentValue` and the onChange event triggers the `handleChange` function.

### Adding state with React hooks
Let's add state to the component by using the `useState` hook.

```jsx
const CurrencyInput: React.FC<Props> = ({
  value: valueFromProp,
  onChange,
}) => {
  const [currentValue, updateCurrentValue] =12 React.useState<string>(
    valueFromProp ? valueFromProp : ""
  );

  return (
    <input
      type="text"
      value={currentValue}
      onChange={handleChange}
    />
  )
}
```

We have initiated the state with either `valueFromProps` or an empty string, but we’re not there yet. The value is a number and the state property expects a string. To solve this, we format the number as string and add a thousands separator with a simple regex expression.

```jsx
const format = (value: number) =>
  value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
```

### Updating the state and pass on data
Now we're about to implement the `handleChange`, a function that updates the state and invokes the callback function `updateValue` from the parent component.

```jsx
const CurrencyInput: React.FC<Props> = ({
  value: valueFromProp,
  updateValue,
}) => {
  const [currentValue, updateCurrentValue] = React.useState<string>(
    valueFromProp ? format(valueFromProp) : ""
  );

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { target: { value }} = event;
  }

  return (
    <input
      type="text"
      value={currentValue}
      onChange={handleChange}
    />
  )
}
```

By destructuring the event object we get the input value. First we need to check if it's empty in which case we pass on `undefined` to our parent component. If the input isn't empty, we can update our state with a formatted string, and send the value to the parent component as a number. To do this we remove the applied formatting and parse it to a number.

```typescript
const stringToNumber = (value: string): number => parseInt(value.replace(/\./g, ""), 10);
```

This is what it looks like when we bring it all together.

```jsx
interface Props {
  value: number | undefined;
  updateValue: (value: number | undefined) => void;
}

const CurrencyInput: React.FC<Props> = ({
  value: valueFromProp,
  updateValue,
}) => {
  const [currentValue, updateCurrentValue] = React.useState<string>(
    valueFromProp ? format(valueFromProp) : ""
  );

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const {
      target: { value }
    } = event;

    if (value === "") {
      updateCurrentValue("");
      return updateValue(undefined);
    }

    const valueAsNumber = stringToNumber(value);

    updateCurrentValue(format(valueAsNumber));
    return updateValue(valueAsNumber);
  };

  return (
    <input
      type="text"
      value={currentValue}
      onChange={handleChange}
    />
  )
}
```

Now we have a component with a text field that shows a formatted string, but handles it as a number.