Let's create a small example to use useRef to bind elements
Tags: react, typescript, reacthooks
Published: 2021-08-23 16:57:50

## Avoid invalid inputs in react

So you want to avoid your user typing invalid characters in, for example, a numeric input?  You only need a custom hook and a regex or a custom function.

Important note!!!: This validation will not replace any validation library and will not work on mobile devices.

your can check the a code and a live demo at [code sandbox](https://codesandbox.io/s/event-listener-for-validation-84x9g?file=/src/App.tsx:0-1109)

{% codesandbox event-listener-for-validation-84x9g%}


## Lets build the custom hook
Let's add a custom hook to add an event listener to a specific element and trigger a function when that event happens.
```
import React, { useEffect } from "react";

type EventListenerParams = {
  action: (event: Event) => void; // what do we want to perform with the event
  event: string; // what event do we want to listen for
  reference: React.RefObject<HTMLInputElement>; // the element that we want to bind to the listener
};

/**
 * Add a event listener for a specific event to a specific element
 */
const useEventListener = (params: EventListenerParams): void => {
  useEffect(() => {
    // on init add event listener
    params.reference.current?.addEventListener(params.event, params.action);
    // on component unmounted, remove the event listener to avoid performance issues
    return () =>
      params.reference.current?.removeEventListener(
        params.event,
        params.action
      );
  }, [params.reference, params.event, params.action]);
};

export default useEventListener;

```

With this custom hook, You just need to pass the parameters for each element or event you want to listen for; don't forget to add the return statement to the custom hook, to remove the event listener at component unmount.

### What's the difference between bind elements with useRef and getElementById?

By selecting the element with `getElementById` you will get the first one that the function finds in the DOM tree, so, if you re-use a component with this validation, it will work only for the first instance

And if you use `React.createRef` you will bind an specific instance of an element, so the biding will be unique for each instance

## Now lets create a couple of validation functions to test our custom hook
The first one to validate the length of an input value
The second one to validate if the value of an input matches with a regex
```
/**
 * Check if the current length of the input value is minor to a fixed length
 * @param maxLength of your input
 */
export function lengthValidator(maxLength: number) {
  return function validation(event: Event): void {
    const element = event.target as HTMLInputElement;
    // if the value length is already equals to maxLength
    if (element.value.length >= maxLength) {
      //  cancel the event and their side effects
      event.preventDefault();
    }
  };
}
/**
 * Check if the current value of the input plus the key pressed match with a pattern
 * @param regex to compare the input value
 */
export function matchValidator(regex: RegExp) {
  return function validation(event: Event): void {
    // get the element
    const element = event.target as HTMLInputElement;
    // get the element current value plus the input key value
    const stringToValidate = String(
      element.value + (event as KeyboardEvent).key
    );
    // if input value does not match with the regex
    if (!stringToValidate.match(regex)) {
      // cancel the event and their side effects
      event.preventDefault();
    }
  };
}

```

## Now lets add some input fields to test our creation
```
import React from "react";
import "./styles.css";
import useEventListener from "./use-event-listener";
import { lengthValidator, matchValidator } from "./validations";

export default function App() {
  const inputRef = React.createRef<HTMLInputElement>();
  const inputRef2 = React.createRef<HTMLInputElement>();
  /**
   * Validates that max-length field never be longer than 10
   */
  useEventListener({
    action: lengthValidator(10),
    event: "keypress",
    reference: inputRef
  });
  /**
   * Validates that numbers-only always contains numeric characters
   */
  useEventListener({
    action: matchValidator(new RegExp("^[\\d]+$")),
    event: "keypress",
    reference: inputRef2
  });
  return (
    <div className="App">
      <div>
        <label>Input for with max length of 10</label>
        <input
          placeholder="Enter a name with no more of 10 characters"
          type="text"
          id="max-length"
          ref={inputRef}
        />
      </div>
      <div>
        <label>Input only for numbers</label>
        <input
          ref={inputRef2}
          placeholder="Enter only numbers"
          type="text"
          id="numbers-only"
        />
      </div>
    </div>
  );
}


```

And that's all, now you can use the custom hook in all the components you want, and you can implement your validation functions.