DEV Community

Cover image for Refs is more than enough
M. Akbar Nugroho
M. Akbar Nugroho

Posted on

Refs is more than enough

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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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).

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>
    );
};
Enter fullscreen mode Exit fullscreen mode

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.

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>
    );
};
Enter fullscreen mode Exit fullscreen mode

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.

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>
    );
};
Enter fullscreen mode Exit fullscreen mode

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.

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 :).

👋 Thanks for reading!

Top comments (0)