Never call a React function component
Tags: react, javascript, webdev
Published: 2020-05-01 18:55:19

When we write React code, we use JSX to create React elements. In case you don’t know, JSX is an **extension to Javascript** to support syntax that **_looks like_ the HTML code** you would write to create DOM elements.

JSX allows us to create a **React element** by just writing this:

```jsx
const element = <div>Hello World</div>;
```

As the browser does not understand JSX natively, [**Babel**](https://babeljs.io/)  then  converts the JSX syntax  using `react.createElement` API.

If you want to know more about JSX and React elements, you can check [this post](https://levelup.gitconnected.com/understanding-react-elements-and-jsx-c3b7af356db4).

## Then we have Components

React allows us to **group** React elements and create **components**. They let you split the UI into **independent**, **reusable** **pieces**, and think about each piece in isolation.

Conceptually, components are like JavaScript functions. They **return React elements describing what should appear on the screen.**

As you may already know, a component can be written as a **class**…

```jsx
import React from "react";

export default class Component extends React.Component {  
  render() {  
    return <h1>Hello World</h1>;  
  }  
}
```

…or as a **function**:

```jsx
import React from "react";

export default function Component() {  
  return <h1>Hello World</h1>  
}
```

But when it comes to **rendering** a component, you always write the same thing, both for classes and function components:

```jsx
...  
return <Component />;
```

**You render your components using JSX syntax.** And this works just fine.

But if you have a function component, can’t you just **_call_ the function**?

Well, you can, but weird things will happen. Let’s take a look.

## Let’s start with a basic app

Our example application will consist of two components: `App` and `Item`. The `App` component will render a **list** of items, and each item is an **input** field. There is also a **button** which allows us to **add** a new item to the list.

```jsx
import React, { useState } from "react";  

import Item from "./Item";import "./styles.css";

export default function App() {  
  const [list, setList] = useState([0]);  
  const addItem = () => setList([...list, list.length]);  
    
  return (  
    <div className="App">  
      <button onClick={addItem}>Add Item</button>  
      <h1>This is the list:</h1>  
      {list.map(_ => Item())}  
    </div>  
  );  
}
```

> **Please note that inside the** `map` **we use to render the list items, we are calling the Item component as a function:** `Item()`.

Our `Item` component is just an input field:

```jsx
import React, { useState } from "react";  

import "./styles.css";

export default function Item() {  
  const text, setText = useState();  
  const handleChange = e => setText(e.target.value);  
  return <input value={text} onChange={handleChange} />;  
}
```

**Please also note that both of our components are using the** `useState` **hook.** This is important.

Here is the finished app so you can play with it:

{%codesandbox call-function-component-0xrqs %}

You may have noticed there is a warning because we are not passing the `key` prop for our Item components.[The key prop is important](https://francodalessio.com/understanding-the-importance-of-the-key-prop-in-react), but **not** the issue I want to talk about here.

Go ahead and click the `Add Item` button. You should get this error:

<img src="https://miro.medium.com/max/548/1*eSuU1eXBY2JOaswUWNh6ZA.png" alt="React complains because all renders must have the same number of Hooks calls" />

Ok, so we know that calling a function component produces some weird error related to Hooks, but what is it exactly? 🤔

## Understanding the problem

If you check the console, you will find more information:

```
Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: [https://fb.me/rules-of-hooks](https://fb.me/rules-of-hooks)  
  
   Previous render            Next render  
   ------------------------------------------------------  
1\. useState                   useState  
2\. useState                   useState  
3\. undefined                  useState  
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
  
    in App (at src/index.js:9)  
    in StrictMode (at src/index.js:8)
```

There are multiple **rules for Hooks**, and an important one is this one:

> You need to make sure that the Hooks are always called the same number of times for a given component.

Do you understand the problem now?

The way we’re calling `Item`, is **not a component** at all, but a function. **React cannot associate any** `useState` **calls** to the `Item` function, just because it’s not rendered as a component.

So the `useState` call that should be associated to `Item`, is actually **associated with** `App`. Now the error message _“Rendered more hooks than the previous render”_ finally makes sense.

This is the reason we **use JSX to render our components**, even if they are function components. That way, React can **register** any Hooks that are used in a component, with the **instance** of that component.

## Ok, got it. But why the app works just fine if you don’t press the button?

You’ve probably noticed that when the app starts, it **works just fine**. You can even write in the input field and you won’t see any error.

This is because the `App` component is **not being re-rendered**. Once we click the button to add a new item, `App` is re-rendered and then React notices that **the number of Hooks calls doesn’t match with the one of the previous render.**

So **there will be cases in which you won’t see an error**, but doing this is still wrong. In our example, at the beginning it works, but **the** `useState` **call of** `Item` **is being associated to** `App`. This is wrong, and it can make your app behave in unexpected ways as you make changes.

## That’s it!

I hope this was useful! Thanks for reading ❤️