Konstantin Dzuin

The problem with React.FunctionComponent

Some time ago I was working on the project to migrate 4 different applications supported by one team to TypeScript. That was a new experience for me and I dived deep into new information, digging and exploring approaches and best practices.

At that moment (early 2019), one of the most comfortable and ‘stable’ approaches was to use React.FunctionComponent (React.FC), which provides all you need just out of the box. It controls the returned type, it controls the children prop.

We used pure functions (almost no classes) in our UI codebase and the migration was quite straight forward. This is a very basic example how you could use FunctionComponent.

import React from 'react';

interface Props {
  extraText: string;
}

export const Component: React.FunctionComponent<Props> = props => (
  <div>
    <div>{props.children}</div>
    <div>{props.extraText}</div>
  </div>
);

That's really helpful interface, it helps a lot and covers many aspects of React components. It takes care of:

  • defining children property
  • validating defaultProps interface
  • defining return type

Sometimes, I faced the situation where consumers tried to pass children to my component, but I didn't expect them. In order to get type validation right, I added children?: never type to the interface. But that part is usually added only after a failure happened. With this helpers it's really easy for me to stop thinking about children property at all.

Let's take a look how this component would look like without FunctionComponent interface.

import React from 'react';

interface Props {
  extraText: string;
  children?: React.ReactNode;
}

export const Component = (props: Props) => (
  <div>
    <div>{props.children}</div>
    <div>{props.extraText}</div>
  </div>
);

I would like to highlight, that this snippet is safe even without specifying the return type. I might use another component and would like to be type safe, in this case I add return type to the arrow function, see the JSX.Element part below. Really, I don't want to get undefined from AnotherComponent and ruin everything.

export const Component = (props: Props): JSX.Element => (
  <AnotherComponent {/* ... */} />
);

That's totally fine until you want to use generic components. You can't do it with React.FunctionComponent unfortunately.