Refs is more than enough
Tags: react, javascript
Published: 2023-02-20 20:13:14

This story started when I learning React.js for the first time. Where I see a lot of influencers overused the `React.useState` or generally speaking, a React.js state.

Let's be honest, we ever put a React state on every HTML input we have. When it has two inputs, no problem occur.

Sometime, your PM tells you to add a new feature and guest what? You add more and more state. Leading your application slow because of unnecessary re-render.

And now, you confuse why the application so slow even crashed on older browser.

...or maybe, Refs is the answer you looking for?

## State: The Sweat Part

No doubt. React state helps us when dealing with interactive UI. For example you have a filter feature. You can filter your data by date, category, etc and display the filtered data.

Normally, your code looks like this.

```jsx
const Filter = () => {
  const [filter, setFilter] = useState(null);

  const fetchDataWithFilter = (e) => {
    setFilter(e.target.value);

    /** Fetching data... */
  }; 

  return (
    <div>
      <p>Filter by: {filter || 'none'}</p>
      <select onChange={fetchDataWithFilter(filter)}>
        <option value="monthly">Monthly<option>
        <option value="cluster">Cluster</option>
      </select>
      {/** data display goes here... */}
    </div>
  );
}
```

As the result, you have beautiful and interactive filter feature.

At this point, theres no thing wrong with this approach. But, what happens when you have a case like managing values inside a form?.

## State: The Sour Part

When you have a case like product entry form, normally you have >= 5 input fields. If you use React state for each input field to listen for value changes it's not good because your form will re-renders many times.

Let's imagine your client types 5 (average) letters for each input field and the form should have 5 (key hits) * 5 (input fields) = 25 (re-renders).

```jsx
const ProductEntry = () => {
    /** States declarations... */

    return (
      <form>
        <input name="name" onKeyUp={e => setName(e.target.value)} />
        <input name="price" onKeyUp={e => setPrice(e.target.value)} />
        <input name="stocks" onKeyUp={e => setStocks(e.target.value)} />
        {/** Rest of input fields... */}
      </form>
    );
};
``` 

The benefit is you always got uptodate values where you can validate the values on the fly. But, you will have performance worst.

## Refs: The Healthy Sweat

Instead of putting states for each input fields, you can use ref for getting the values without causing re-renders.

```jsx
const ProductEntry = () => {
    /** Refs declarations... */

    return (
      <form>
        <input name="name" ref={inputNameRef} />
        <input name="price" ref={inputPriceRef} />
        <input name="stocks" ref={inputStocksRef} />
        {/** Rest of input fields... */}
      </form>
    );
};
```
With this approach, no matter how much your client types in the input fields, React shouldn't re-renders the form.

The downside is you need to add extra step for process the values (validate, sanitize, etc).

We can use a pattern called observer to handle the downside. Instead of process the values on the fly, we process it when some event fired. Normally when the form is being submitted.

```jsx
const ProductEntry = () => {
    /** Refs declarations... */

    const submit = () => {
      // 1. Collect all values
      // 2. Validate
      // 3. Sanitize
      // 4. Perform HTTP request
    };

    return (
      <form>
        <input name="name" ref={inputNameRef} />
        <input name="price" ref={inputPriceRef} />
        <input name="stocks" ref={inputStocksRef} />
        {/** Rest of input fields... */}
        <button onClick={submit}>Save Product</button>
      </form>
    );
};
```
So, before the button is clicked, no actions is running and less of re-renders.

## Objectives

### Use state wisely

State makes our app interactive and it's awesome. When dealing with large app, you should pay attention of every state change.

Too many re-renders is not good for you app. Except you isolate it, so others parts not affected.

### Use refs with observer pattern

It sounds every technical, but actually it's very simple concept. Just run your functions when an event fired.

- `Button clicked -> run submit() function`.
- `Dropdown changed -> run fetchDataWithFilter() function`.

Inside those functions, you can access your refs and process it as usual.

### Use form validation library

Don't so diligent. You shouldn't to manually validate your form. Use a correct library instead. One of my recommendation is [react-hook-form](https://react-hook-form.com/).

It implement refs and observer pattern. So don't be worry of unnecessary re-renders. And also I have wrote a guide how to use it :).

{% embed https://dev.to/thexdev/form-validation-with-react-hook-form-94e %}

👋 Thanks for reading!