Top three React & TypeScript pitfalls
Tags: react, typescript, javascript, ts
Published: 2021-06-02 08:29:47

---
title: Top three React & TypeScript pitfalls
published: true
description: Mind those pitfalls while working with React & TypeScript.
tags: react, typescript, javascript, js, ts
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7d04ormc3nu2mgqhux2.jpeg
---
The usage of React & TypeScript exploded in recent years. This should not come as a surprise to anyone at this point. Both tools proven to be viable working on web application large and small allowing developers to satisfy various business needs.

With the explosion of popularity comes also the explosion of mistakes that engineers can make while working with this stack in their day-to-day jobs. This blog aims to shed a light on my top three React & TypeScript pitfalls I’ve seen developers fall into and how they could be avoided.

Let us start with the most important one.

## Using `React.FunctionComponent` or `React.FC`

I often see components being annotated as such:

```tsx
import * as React from 'react'

type Props = {
    // ...
}

const FirstComponent = React.FC<Props> = (props) => {
    // ...
}

const SecondComponent = React.FunctionComponent<Props> = (props) => {
    // ...
}
```

At first glance, it might seem like a good idea to type your components using these _type abstractions_. The ergonomics of `React.FC` and `React.FunctionComponent` might undoubtedly be tempting. By default, they provide you with typings for the `children` prop, the `defaultProps`, `propTypes`, and many other component properties.

> You can read more about the React.FC and React.FunctionComponent [here](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components).

With all that being said, I believe that they introduce unnecessary complexity and are too permissive in terms of types.

Let us start with the most critical issue using either `React.FC` or `React.FunctionComponent`. I’m talking about the unnecessary complexity they introduce. Here is a simple question: which type of annotation feels more straightforward and easier to digest to you?

The one that where we explicitly annotate components arguments:

```tsx
type Props = {
  // ...
};

const Component = (props: Props) => {
  // ...
};
```

Or maybe the one where we use `React.FC`

```tsx
import * as React from "react";

type Props = {
  // ...
};

const Component: React.FC<Props> = props => {
  // ...
};
```

If you are familiar with `React.FC`, you might shrug your shoulders and say that both of them are completely valid options. And this is where the problem lies, mainly in the concept of _familiarly_ or lack thereof.

The `React.FC` interface is shallow. In most cases, it can be replaced by annotating props explicitly. Now, imagine being new to a codebase, where `React.FC` is used extensively, but you have no idea what it means and what it does. You would most likely not be comfortable amending the `Props` type definitions within that codebase on your first day.

Another problem these typings introduce is the implicit composability by augmenting the `Props` definition with the `children` property.

I love how composable React components can be. Without the `children` property, it would be pretty hard to achieve one of my favorite patterns in React, the _[compound components pattern](https://kentcdodds.com/blog/compound-components-with-react-hooks)_. With that in mind, I believe that we introduce misdirection to their APIs by making the composition of components implicit.

```tsx
import * as React from "react";

const MarketingButton: React.FC<{}> = () => {
  // Notice that I'm not using `props.children`
  return <span>Our product is the best!</span>;
};

// In a completely separate part of the codebase, some engineer tries to use the `MarketingButton`.
const Component = () => {
  return <MarketingButton>HELLO!??</MarketingButton>;
};
```

The engineer consuming the API would most likely be confused because, despite being able to pass the _children_ in the form of a simple string, the change is not reflected in the UI. To understand what is going on, they would have to read the definition of the `MarketingButton` component - this is very unfortunate. It might seem like a contrived example, but imagine all the seconds lost by thousands of engineers each day going through what I’ve just described. This number adds up!

## Typing the children property wrong

In the last section, I touched on how important the `children` prop is. It is then crucial to correctly annotate this property to make other developer’s work with life easier.

I personally have a simple rule that I follow that works for me:

> Use React.ReactNode by default. Change if necessary

Here is an example

```tsx
type Props = {
  children: React.ReactNode;
};

const MarketingButton = ({ children }) => {
  return <button>{children}</button>;
};
```

I find myself opting out of `React.ReactNode` very rarely, primarily to further constrain the values of the `children` prop. You can find a great resource to help you pick what type of the `children` prop you should use [here](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/basic_type_example#useful-react-prop-type-examples).

## Leaking component types

How often do you encounter a component written in a following way:

```tsx
export type MyComponentProps = {
  // ...
};

export const MyComponent = (props: MyComponentProps) => {
  // ...
};

// Some other part of the codebase, possibly a test file.
import { MyComponentProps } from "../MyComponent";
```

Exporting the `MyComponentProps` creates two problems.

1. You have to come up with a name for the type. Otherwise, you will end up with a bunch of exported symbols that all have the same name. Operating in such a codebase is cumbersome because you have to actively pay attention to where the auto-completion imports the symbols from.
2. It might create implicit dependencies that other engineers on your team might not be aware of.
   - _Can I change the name of the type?_
   - _Is `MyComponentProps` type used somewhere else?_

Whenever you keep the type of the props _un-exported_, you avoid those issues.

There exists a mechanism that allows you to extract the type of props for a given component without you having to use the `export` keyword. I’m referring to `React.ComponentProps` generic type. The usage is as follows.

```tsx
type Props = {
  // ...
};

export const MyComponent = (props: Props) => {
  // ...
};

// In another file.
import { MyComponent } from "../MyComponent";
type MyComponentProps = React.ComponentProps<typeof MyComponent>;
```

I’ve been using this technique for the past two years that I’ve been writing React & TypeScript code, and I have never looked back. You can read more about how useful this generic type is in the context of writing component tests in [one of my other blog posts](https://dev.to/wojciechmatuszewski/writing-unit-tests-with-react-typescript-and-react-testing-library-1nmg).

## Summary

These were the top three pitfalls I’ve most commonly seen in the wild. I hope that you found my ramblings helpful.

If you noticed that something I’ve written is incorrect or would like to clarify a part of the article, please reach out! 

You can find me on twitter - [@wm_matuszewski](https://twitter.com/wm_matuszewski)

Thank you for your time.