252

I am learning TypeScript and some bits are confusing to me. One bit is below:

interface Props {
  name: string;
}

const PrintName: React.FC<Props> = (props) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

const PrintName2 = (props: Props) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

For both functional components above, I see TypeScript generates the same JS code. The PrintName2 component seems more streamlined to me as far as readability. I wonder what's the difference between the two definitions and if anyone is using second type of React component?

2

5 Answers 5

217

Thanks all for the answers. They are correct but I was looking for a more detailed version. I did some more research and found this on React+TypeScript Cheatsheets on GitHub.

Function Components
These can be written as normal functions that take a props argument and return a JSX element.

type AppProps = { message: string }; /* could also use interface */

const App = ({ message }: AppProps) => <div>{message}</div>;

What about React.FC/React.FunctionComponent? You can also write components with React.FunctionComponent (or the shorthand React.FC):

const App: React.FC<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);

Some differences from the "normal function" version:

It provides typechecking and autocomplete for static properties like displayName, propTypes, and defaultProps - However, there are currently known issues using defaultProps with React.FunctionComponent. See this issue for details - scroll down to our defaultProps section for typing recommendations there.

It provides an implicit definition of children (see below) - however there are some issues with the implicit children type (e.g. DefinitelyTyped#33006), and it might be considered a better style to be explicit about components that consume children, anyway.

const Title: React.FunctionComponent<{ title: string }> = ({
  children,
  title
}) => <div title={title}>{children}</div>;

In the future, it may automatically mark props as readonly, though that's a moot point if the props object is destructured in the parameter list.

React.FunctionComponent is explicit about the return type, while the normal function version is implicit (or else needs additional annotation).

In most cases, it makes very little difference which syntax is used, but the React.FC syntax is slightly more verbose without providing clear advantage, so precedence was given to the "normal function" syntax.

Sign up to request clarification or add additional context in comments.

5 Comments

Hey Kuldeep, first of all thanks for the detailed answer. Maybe it is missing a link for "See this issue for details ". If I'm mistaken please disregard :)
Hey, the link for detailed explanation is the github link I mentioned in first paragraph. :) Thanks
Since React 18 an implicite children prop is removed. See: github.com/DefinitelyTyped/DefinitelyTyped/pull/56210
How do you access the children from now on? Should I manually add a children: ReactNode property in my type definition?
Hey Kuldeep, first of all thanks for the detailed answer. Maybe it is missing a link for "See this issue for details ". If I'm mistaken please disregard :)
93

React.FC is not the preferable way to type a React component, here's a link.

I personally use this type:

const Component1 = ({ prop1, prop2 }): JSX.Element => { /*...*/ }

Short list of React.FC cons:

  1. Provides an implicit definition of children, even if your component doesn't need to have children. That might cause an error.
  2. Doesn't support generics.
  3. Doesn't work correctly with defaultProps.

8 Comments

How to you type check prop1, prop2 etc?
Use React.VFC if you do not use the 'children' prop
1. Every react element has children, look at React.createElement. If you don't want them, don't render them. Or use React.VFC. 2. It is generic by design xD. But any way it is not a problem of React.FC, it is a problem of .tsx syntax. It fails if you want to make an lambda function with generic property. 3. defaultProps it is runtime part. Setting props from some external magic source is brrrr.. I will never understand this, because I can set defaults right in component.
React.FC supports generics, you can do React.FC<Props>
Addressing some of the comments on this answer: As of React 18 React.VFC is deprecated. github.com/DefinitelyTyped/DefinitelyTyped/pull/59882
|
43

Best solution:

The second approach + a return type

const PrintName2 = ({ prop1, prop2 }: Props): JSX.Element => { /** */}

This gives you complete control over the Props and is more explicit (there is no hidden magic).

Link to GitHub PR to remove React.FC

Kent C Dodds has some thoughts why this is here

Sub-optimal solution:

The first version (React.FC) will add a few types for you - they come from the React Typings

interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement | null;
  propTypes?: WeakValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}

This approach is useful for a few reasons but an obvious one is if your component has children, then you don't need to manually add a children prop. It's not great because there are issues with default props and many components don't have children.

4 Comments

This is no longer the case, React.FC no longer includes the children prop! :D
@cdimitroulas would you recommend using React.FC now?
I have been using it everywhere, yes. I think it works great and gives good type safety to ensure you always return a valid value from all code paths. There is just one issue which is that for some reason it doesn't allow you to return a plain string from a component which should be valid
Explicitly adding : JSX.Element for a return type is not even required because TS is smart enough to figure it out by the return value of the component.
14

Since you are using React and TypeScript, you should always use the first pattern, as it will ensure that your component is more strictly typed since it implies that the PrintName will be of type React Functional Component, and it takes in props of type Props.

const PrintName: React.FC<Props>

You may read the full interface definition for Functional Components on the React TypeScript typings repository.

The second example you have provided does not provide any form of typings, except that it is a function that takes a set of parameters of type Props, and that it can return anything in general.

Therefore, writing

const PrintName2 = (props:Props)

is akin to

const PrintName2: JSX.Element = (props:Props) 

as TypeScript is definitely unable to automatically infer that it is a Functional Component.

In addition the FC provides typings for displayName and ensure the proper return type. Without FC the following is valid TypeScript:

export const HapticTabF = (props: BottomTabBarButtonProps) => {
  return { "abc": "def" }
}
HapticTabF.displayName = {"AKJASDKL":"ASD"};

The only major gap of FC is it does not designate the Props as Readonly which means the following is allowed.

export const HapticTab: FC<BottomTabBarButtonProps> = (props) => {
  props.style = {"shadowColor":"123"}
}

But having it be export const HapticTab: FC<Readonly<BottomTabBarButtonProps>> ... is too verbose and is better off that React fixes it in their libraries.

4 Comments

OK. but return type in second case should be inferred. hovering over it shows below: const PrintName2: (props: Props) => JSX.Element
While it will be inferred, sometimes inference is not fully correct but also if you add the type information first before you add the return value, you will get errors until you meet the return type specification.
It may infer it as a jsx element, but it does not automatically infer it as a functional component
Best answer, straight forward
10

Don't use React.FC anymore. The reason why people used to use React.FC was because it added more types like children to your props.

in your code example you don't need children so just write it like this

const PrintName: (props: {name:string; priority: "bold" | "normal"}) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

but the problem you will eventually have is that you will want to pass in children to your components and the preferred way of doing this is like this

interface Props extends React.PropsWithChildren {
   name: string
   priority: "bold" | "normal"
}

const PrintName: (props: Props) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}> 
        {props.children}
      </p>
    </div>
  )
}

extending with React.PropsWithChildren is the same as if you typed:

interface Props {
   name:string
   priority: "bold" | "normal"
   children?: ReactNode
}

React.PropsWithChildren can also be passed a generic but the default is unknown. If you want you can set children like this, but unless you are sure you only want a specific type I would just use the default.

React.PropsWithChildren<Button> 

and pass in the type of children you want in your component.

You do not have to pass a return type of JSX.Element, because it's inferred.

1 Comment

So basically React.FC is old thing? I saw many frameworks using it and thought it gave some kind of significant advantage.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.