Streamlining Constructors in Functional React Components
Tags: react, javascript, tutorial, webdev
Published: 2023-02-08 08:51:25

Several years ago, I wrote an article about how to create constructor-like functionality in React with function-based components.  (You can read it here: https://dev.to/bytebodger/constructors-in-functional-components-with-hooks-280m)  I won't repeat the entire contents of that article, but the basic premise was this:

1. Class-based React components give you the ability to use a constructor.

1. Function-based React components have no direct equivalent to the constructor.  (Because functions, by their very nature, don't _have_ constructors.)

1. The core React documentation implies that you simply don't _need_ constructors anymore.  And to be fair, _usually_ you don't need them.  But it's myopic to imply that no developer, writing function-based components, will _EVER_ need to use constructor-like functionality.

Also to be clear, this is what I'm defining as a "constructor":

1. Code that runs _before anything else in the life-cycle of this component_.

1. Code that runs once, _and only once_, for the entire life-cycle of this component.

In that article, I demonstrated a technique to "spoof" constructor-like functionality in function-based components.  In fact, I even created an NPM package for my `useConstructor()` feature.  

Some of you may scoff at this idea, owing to React's official stance that constructors simply aren't needed - at all - in modern React applications.  However, a funny thing happened after I published that article.

That article became my second-most-read post.  To-date, it's received almost 90,000 views.  In case it's not clear, this is a strong indication that:

1. Many people still wonder how to implement constructor-like functionality in function-based React components (regardless of the fact that the core documentation claims that you don't _need_ them at all).

1. And apparently, there are many people out there _Googling_ on this subject.  Because most of the views that I've received on that article have come, in a steady stream, over many months (and... _years_) since I wrote the original article. 

So why am I writing on the subject again???

Well, it turns out that my first solution was not "bad".  But it was also, umm... suboptimal.  Overly complicated.  A bit convoluted, if you will.  So I'm going to show you a simpler way to create "constructors" in function-based React components, without leveraging state values or NPM packages.
<br/><br/>



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3p088f7b14a9lgw4zv6g.jpeg)
## The Sample Application

(**NOTE:**  If you want to see a live example of all the subsequent code, you can check it out here:  https://stackblitz.com/edit/constructor-functionality-in-react-functional-components)

Our dead-simple example will just have one component that looks like this:

```javascript
export const App = () => {
  const [counter, setState] = useState(0);

  const increment = () => setState(counter + 1);

  return <>
    <div>
      Counter: {counter}
    </div>
    <div>
      <button onClick={increment}>
        Increment
      </button>
    </div>
    <Child/>
  </>;
};
```

This is just your standard demo/counter app.  It renders a state value for the current value of `counter`.  And it gives you a button that you can click to increment that value.  

So let's imagine that you have some bit of "pre-processing" that you want to happen the first time that `<App/>` is invoked.  Remember, in keeping with our guidelines for what a constructor should _do_, we want this pre-processing to happen _before_ anything else in the component.  (Essentially, we want the pre-processing to occur before the initial render.)  We also want the pre-processing to run once, _and only once_, for the entire lifecycle of the component.  This is where `useRef()` will come in handy.
<br/><br/>



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/heeb1e2qd0asuq032f9z.jpg)
## Potential Solutions

A ref is a value that remains in memory _between renders_.  It's not the same as a _state_ variable - because updating state variables triggers the reconciliation process.  In other words, you use state variables when you have values that will influence the display.  But you use refs when you have values that should stay in memory - _but are not used in the display_.

Utilizing `useRef()`, our proposed constructor would look like this:

```javascript
export const App = () => {
  const [counter, setState] = useState(0);
  const constructorHasRun = useRef(false);

  const constructor = () => {
    if (constructorHasRun.current !== false) 
      return;
    constructorHasRun.current = true;
    // put your constructor code HERE
    console.log('constructor invoked at ', window.performance.now());
  };

  constructor();

  const increment = () => setState(counter + 1);

  return <>
    <div>
      Counter: {counter}
    </div>
    <div>
      <button onClick={increment}>
        Increment
      </button>
    </div>
  </>;
};
```

When the app loads, you can see in the console that the constructor only runs _once_.  No matter how many times you click the "Increment" button thereafter, the constructor logic is never triggered again.

[**NOTE:** If you are in strict mode, React will call this function _twice_.  That can lead to some confusion while you're developing. This is expected behavior that React uses to "help you find accidental impurities".  _This is development-only behavior and does not affect production._]

The `constructorHasRun` ref value serves as a tracking variable.  Once it's set from `false` to `true`, the constructor code will never run again for the lifecycle of the app.

This approach works just fine - but it still feels a little clunky to me.  Under this approach, you must first define the constructor function, and then _remember_ to invoke it somewhere before the `return` statement.  Ideally, you'd probably like your constructor code to just... _run_.  Luckily, we can achieve this outcome by using an Immediately Invoked Function Expression (IIFE).  That would look like this:

```javascript
export const App = () => {
  const [counter, setState] = useState(0);
  const constructorHasRun = useRef(false);

  (() => {
    if (constructorHasRun.current !== false) 
      return;
    constructorHasRun.current = true;
    // put your constructor code HERE
    console.log('constructor invoked at ', window.performance.now());
  })();

  const increment = () => setState(counter + 1);

  return <>
    <div>
      Counter: {counter}
    </div>
    <div>
      <button onClick={increment}>
        Increment
      </button>
    </div>
  </>;
};
```

<br/><br/>![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i767urf31km4pytwrtm9.jpg)
## The _Best_ Solution

I added this section after originally publishing this article because @miketalbot recommended this in the comments.  And honestly, it's cleaner, simpler, and just more _elegant_ than using a tracking variable with `useRef()`.  Here's his recommendation:

```javascript
export const App = () => {
  const [counter, setState] = useState(0);

  useMemo(() => {
    // put your constructor code HERE
    console.log('constructor invoked at ', window.performance.now());
  }, []);

  const increment = () => setState(counter + 1);

  return <>
    <div>
      Counter: {counter}
    </div>
    <div>
      <button onClick={increment}>
        Increment
      </button>
    </div>
  </>;
};
```

This works so well because `useMemo()` is used to cache the _result_ of a computation.  That means that, in order to first _create_ the cache, `useMemo()` will first _run_ the code.  Then, because the dependency array is empty, the cached computation will never be re-run again.