Rethinking TypeScript Practices: The Downside of React.FC
Tags: webdev, typescript, react, bestpractices
Published: 2023-06-20 05:21:18

In recent years, using [TypeScript with React](https://www.typescriptlang.org/docs/handbook/react.html) has become increasingly popular. TypeScript adds static typing to JavaScript, making it more robust and developer-friendly. One of TypeScript's constructs for React components is `React.FC`, which stands for "React Function Component". This type definition has been widely used, but there's a new school of thought that suggests avoiding it. Let's discuss why.

---

## What is React.FC?

In TypeScript, `React.FC` or `React.FunctionComponent` is a type that represents a function component. It can be used like this:

```
const MyComponent: React.FC = () => {
    return <div>My Component</div>;
};
```

---

## The Downside of React.FC

While `React.FC` seems convenient, it has certain downsides that might make you reconsider its usage. These were highlighted in a [Pull Request](https://github.com/facebook/create-react-app/pull/8177) on the Create React App repository back in 2019.

**Implicit Definition of Children**

When you use `React.FC`, it implicitly includes `children` in the props, even if you don't need them. It means all components accept `children` as a prop. This can lead to unwanted renderings in your component and is not caught by TypeScript due to `React.FC`.


```
const App: React.FC = () => <div>Hello World</div>;

// Even though `App` doesn't expect children, it will still render them.
const Example = () => (
  <App>
    <div>Unwanted children</div>
  </App>
);

```

The `Example` component will render "Unwanted children" even though `App` is not designed to accept `children`.


**Doesn't Support Generics**

React.FC does not preserve unresolved generics, so you can't create a generic component like this one that accepts a `prop` of any type and a `callback` function to handle the prop:


```
type GenericComponentProps<T> = {
  prop: T;
  callback: (t: T) => void;
}

// The following will cause an error
const GenericComponent: React.FC<GenericComponentProps> = ({ prop, callback }) => {
  // Some component logic here
};

```

**Makes "component as namespace pattern" more awkward**


This pattern, which is quite popular, allows a component to be used as a namespace for related components. Using `React.FC` makes this awkward:

```
type SelectProps = { /* ... */ };
type ItemProps = { /* ... */ };

// With React.FC
const Select: React.FC<SelectProps> & { Item: React.FC<ItemProps> } = (props) => {/* ... */ }
Select.Item = (props) => { /*...*/ }

// Without React.FC
const Select = (props: SelectProps) => {/* ... */}
Select.Item = (props: ItemProps) => { /*...*/ }

```

**Doesn't Work Correctly with `defaultProps`**

The `defaultProps` mechanism in React is used to define default values for `props` in a component. It is useful when you want some `props` to have a default value, but also want to allow consumers of your component to override these defaults.

The issue with React.FC arises when we combine TypeScript and defaultProps.

```
type ComponentProps = { name: string; }

const Component: React.FC<ComponentProps> = ({ name }) => (<div>{name.toUpperCase()}</div>);

Component.defaultProps = { name: "John" };

// usage
<Component /> // TypeScript error: Property 'name' is missing...

```

In this example, TypeScript considers `name` as required and doesn't acknowledge that `defaultProps` provides a default value for it. So, when we try to use `<Component />` without providing a name prop, TypeScript will give an error, even though this should be valid due to the defaultProps.

---

## The Alternative to React.FC

A cleaner way to define function components in TypeScript is to type the props and the function's return value directly:

```
type MyComponentProps = {
  /* define your prop types here */
};

const MyComponent = (props: MyComponentProps): JSX.Element => {
  /* ... */
};

```

This alternative doesn't have the downsides associated with `React.FC`. It doesn't implicitly include `children`, supports generics, allows the "component as namespace" pattern, and works correctly with `defaultProps`.

---

## Conclusion

Navigating the realm of TypeScript with React can feel a bit like deciphering a new language. On our journey, we initially encounter `React.FC`, a seemingly helpful guide that promises to make our path smoother. However, as we dig deeper and our understanding grows, we come to recognize its limitations - its automatic inclusion of `children` props, the lack of generics support, its awkward relationship with the 'component as namespace' pattern, and its deficiency in handling `defaultProps` effectively.

In software development, each tool we use, each methodology we adopt, is in service of creating efficient, reliable, and maintainable code. We owe it to ourselves and to the robust applications we aim to build, to select the most reliable and beneficial practices.

Instead of `React.FC`, a more flexible and robust alternative presents itself—directly typing the component function and its props. This approach doesn't suffer from the constraints of `React.FC` and supports our components in all their various forms and functionalities.

Let's remember, the landscape of technology is always shifting. The tools and approaches that seemed infallible yesterday might be revealed as imperfect today. This doesn't negate their prior value but instead underscores the evolutionary nature of software development. The shift away from `React.FC` towards more explicit typing isn't a dismissal, but rather a symbol of our growing understanding and maturity in using TypeScript with React.

As we continue our journey through the diverse landscape of TypeScript and React, let's stay adaptable, keep our knowledge updated, and strive for the practices that enable us to build better, more reliable software. Even if it means parting ways with some old habits, the benefits gained from this evolution will be worth the effort. Let the dance of development continue!