React: Calling functional components as functions
Tags: javascript, react, webdev
Published: 2020-08-26 23:58:12

TL;DR

To be a component ≠ Return JSX
`<Component />` ≠ `Component()`

---

**Note**: This article tries to explain a somewhat advanced concept.

One of my favourite things in web development is that almost any question can lead to an unforgettable deep dive that will reveal something completely new about a very familiar thing.

That just happened to me, so now I know a tiny bit more about React & want to share it with you.

It all started with a bug which we are going to reproduce now step by step. Here is the starting point:
{% codesandbox b0rl9 module=/src/App.js %}

This app contains just 2 components `App` & `Counter`.

Let's inspect `App`'s code:

```React
const App = () => {
  const [total, setTotal] = useState(0);
  const incrementTotal = () => setTotal(currentTotal => currentTotal + 1);

  return (
    <div className="App">
      <div>
        <h4>Total Clicks: {total}</h4>
      </div>
      <div className="CountersContainer">
        <Counter onClick={incrementTotal} />
        <Counter onClick={incrementTotal} />
        <Counter onClick={incrementTotal} />
      </div>
    </div>
  );
};
```

Nothing interesting as for now, right? It just renders 3 `Counter`s & keeps track and displays the sum of all counters.

Now let's add a brief description to our app:

```diff
const App = () => {
  const [total, setTotal] = useState(0);
  const incrementTotal = () => setTotal((currentTotal) => currentTotal + 1);
+ const Description = () => (
+   <p>
+     I like coding counters!
+     Sum of all counters is now {total}
+   </p>
+ );

  return (
    <div className="App">
      <div>
        <h4>Total Clicks: {total}</h4>
+       <Description />
      </div>
      <div className="CountersContainer">
        <Counter onClick={incrementTotal} />
        <Counter onClick={incrementTotal} />
        <Counter onClick={incrementTotal} />
      </div>
    </div>
  );
};
```

Works perfectly as before, but now it has a shiny new description, cool!

You might notice that I declared component `Description` instead of just writing JSX straight inside `App`'s return statement.
There might be plenty of reasons for that, let's just say that I wanted to keep JSX inside `App`'s return clean & easily readable, so, I moved all messy JSX inside `Description` component.

You could also notice that I declared `Description` **inside** `App`. It's not a standard way, but `Description` needs to know the current state to display total clicks.
I could refactor it and pass `total` as a prop, but I don't plan to ever reuse `Description` because I need just one for the whole app!

Now, what if we also wanted to display some additional text above the central counter? Let's try to add it:

```diff
const App = () => {
  const [total, setTotal] = useState(0);
  const incrementTotal = () => setTotal((currentTotal) => currentTotal + 1);
  const Description = () => (
    <p>
      I like coding counters!
      Sum of all counters is now {total}
    </p>
  );
+
+ const CounterWithWeekday = (props) => {
+   let today;
+   switch (new Date().getDay()) {
+     case 0:
+     case 6:
+       today = "a weekend!";
+       break;
+     case 1:
+       today = "Monday";
+       break;
+     case 2:
+       today = "Tuesday";
+       break;
+     default:
+       today = "some day close to a weekend!";
+       break;
+   }
+
+   return (
+     <div>
+       <Counter {...props} />
+       <br />
+       <span>Today is {today}</span>
+     </div>
+   );
+ };

  return (
    <div className="App">
      <div>
        <h4>Total Clicks: {total}</h4>
        <Description />
      </div>
      <div className="CountersContainer">
        <Counter onClick={incrementTotal} />
-       <Counter onClick={incrementTotal} />
+       <CounterWithWeekday onClick={incrementTotal} />
        <Counter onClick={incrementTotal} />
      </div>
    </div>
  );
};

```

Brilliant! Now we do have a bug! Check it out:
{% codesandbox 450vl module=/src/App.js %}

Note how `total` is incremented when you click on the central counter, but the counter itself always stays at 0.

Now, what surprised me is not the bug itself, but rather that I accidentally found out that the following works seamlessly:

```diff
  return (
    <div className="App">
      <div>
        <h4>Total Clicks: {total}</h4>
        <Description />
      </div>
      <div className="CountersContainer">
        <Counter onClick={incrementTotal} />
-       <CounterWithWeekday onClick={incrementTotal} />
+       { CounterWithWeekday({ onClick: incrementTotal }) }
        <Counter onClick={incrementTotal} />
      </div>
    </div>
  );
```

Surprised as well? Let's dive into together!

## The bug

The bug happens because we create brand new `CounterWithWeekday` on each `App` update.
This happens because `CounterWithWeekday` is declared inside `App` which [might be considered an anti-pattern](https://dev.to/borasvm/react-create-component-inside-a-component-456b).

In this particular case, it's easy to solve. Just move `CounterWithWeekday` declaration outside of the `App`, and the bug is gone.

You might wonder why we don't have the same problem with `Description` if it is also declared inside the `App`.
We actually do! It's just not obvious because React re-mounts the component so fast, that we can't notice and since this component has no inner state, it's not getting lost as in case of `CounterWithWeekday`.

But why directly calling `CounterWithWeekday` resolves the bug as well? Is it documented somewhere that you can just call a functional component as a plain function? What is the difference between the 2 options? Shouldn't a function return exactly the same thing disregard of the way it's invoked? 🤔

Let's go step by step.

## Direct invocation

From React documentation we know that component is just a plain JS class or function that eventually returns JSX (most of the time).

However, if functional components are just functions, why wouldn't we call them directly? Why do we use `<Component />` syntax instead?

It turns out to be that [direct invocation was quite a hot topic for discussion](https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f) in earlier versions of React. In fact, the author of the post [shares a link to a Babel plug-in](https://medium.com/@knoopx/fyi-https-babeljs-io-docs-plugins-transform-react-inline-elements-3ba87132acf5) that (instead of creating React elements) helps in calling your components directly.

I haven't found a single mention about calling functional components directly in React docs, however, there is one technique where such a possibility is demonstrated - [render props](https://reactjs.org/docs/render-props.html).

After some experiments, I came to quite a curious conclusion.

## What is a Component at all?

Returning JSX, accepting props or rendering something to the screen has nothing to do with being a component.

The same function might act as a component & as plain function at the same time.

Being a component has much more to do with having own lifecycle & state.

Let's check how `<CounterWithWeekday onClick={incrementTotal} />` from the previous example looks like in React dev tools:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/q1ro63ps57evv75o8dyc.png)

So, it's a component that renders another component (`Counter`).

Now let's change it to `{ CounterWithWeekday({ onClick: incrementTotal }) }` and check React devtools again:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nuo274mhmtxe1cbvbmd0.png)

Exactly! There's no `CounterWithWeekday` component. It simply doesn't exist.

The `Counter` component and text returned from `CounterWithWeekday` are now direct children of `App`.

Also, the bug is gone now because since `CounterWithWeekday` component doesn't exist, the central `Counter` doesn't depend on its lifecycle anymore, hence, it works _exactly the same_ as its sibling `Counter`s.

Here are a couple of quick answers to the questions I've been struggling with. Hope it'll help someone.

#### Why `CounterWithWeekday` component is not displayed in React dev tools anymore?

The reason is that it's **not a component** anymore, it's just a function call.

When you do something like this:
```react
const HelloWorld = () => {
  const text = () => 'Hello, World';

  return (
    <h2>{text()}</h2>
  );
}
```
it's clear that the variable `text` is not a component.
If it would return JSX, it wouldn't be a component.
If it would accept a single argument called `props`, it wouldn't be a component either.

A function that _could be_ used as a component is not necessarily will be used as a component. So, to be a component, it needs to be used as `<Text />` instead.

Same with `CounterWithWeekday`.

By the way, components can return plain strings.

#### Why Counter doesn't lose state now?

To answer that, let's answer why `Counter`'s state was reset first.

Here is what happens step by step:
1. `CounterWithWeekday` is declared inside the `App` & is used as a component.
2. It is initially rendered.
3. With each `App` update, a new `CounterWithWeekday` is created.
4. `CounterWithWeekday` is a brand new function on each `App` update, hence, React can't figure out that it's the same component.
5. React clears `CounterWithWeekday`'s  previous output (including its children) and mounts new `CounterWithWeekday`'s output on each `App` update. So, unlike other components, `CounterWithWeekday` is never updated, but always mounted from scratch.
6. Since `Counter` is re-created on each `App` update, its state after each parent update will always be 0.

So, when we call `CounterWithWeekday` as a function, it is also re-declared on each `App` update, however, it doesn't matter anymore. Let's check hello world example once again to see why:
```react
const HelloWorld = () => {
  const text = () => 'Hello, World';

  return (
    <h2>{text()}</h2>
  );
}
```

In this case, it wouldn't make sense for React to expect the `text` reference to be the same when `HelloWorld` is updated, right?

In fact, React _cannot_ even check what `text` reference is. It doesn't know that `text` exists at all. React literally wouldn't notice the difference if we would just inline `text` like this:
```diff
const HelloWorld = () => {
- const text = () => 'Hello, World';
-
  return (
-   <h2>{text()}</h2>
+   <h2>Hello, World</h2>
  );
}
```

So, by using `<Component />` we make the component visible to React. However, since `text` in our example is just called directly, React will never know about its existence.
In this case, React just compares JSX (or text in this case). Until the content returned by `text` is the same, nothing gets re-rendered.

That exactly what happened to `CounterWithWeekday`. If we don't use it like `<CounterWithWeekday />`, it is never exposed to React.

This way, React will just compare **the output** of the function, but not the function itself (as it would, in the case if we use it as a component).
Since `CounterWithWeekday`'s output is ok nothing gets re-mounted.

## Conclusion

+ A function that returns JSX might **not** be a component, depending on how it is used.

+ To be a component function returning JSX should be used as `<Component />` and not as `Component()`.

+ When a functional component is used as `<Component />` it will have a lifecycle and can have a state.

+ When a function is called directly as `Component()` it will just run and (probably) return something. No lifecycle, no hooks, none of the React magic. It's very similar to assigning some JSX to a variable, but with more flexibility (you can use if statements, switch, throw, etc.). 

+ [Using state in a non-component is dangerous](https://kentcdodds.com/blog/dont-call-a-react-function-component/).

+ Using functions that return JSX without being a component [might be officially considered to be an anti-pattern in the future](https://github.com/yannickcr/eslint-plugin-react/issues/578). There are edge cases (like render props), but generally, you almost always want to refactor those functions to be components because it's the recommended way.

+ If you have to declare a function that returns JSX inside a functional component (for instance, due to tightly coupled logic), directly calling it as `{component()}` could be a better choice than using it as `<Component />`.

+ Converting simple `<Component />` into `{Component()}` might be very handy for debugging purposes.