Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • C create-react-app
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 1,547
    • Issues 1,547
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 417
    • Merge requests 417
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Meta
  • create-react-app
  • Merge requests
  • !8177

Remove React.FC from Typescript template

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Administrator requested to merge github/fork/Retsam/patch-1 into master Dec 13, 2019
  • Overview 12
  • Commits 1
  • Pipelines 0
  • Changes 1

Created by: Retsam

This removes React.FC from the base template for a Typescript project.

Long explanation for a small change:

React.FC is unnecessary: it provides next to no benefits and has a few downsides. (See below.) I see a lot of beginners to TS+React using it, however, and I think that it's usage in this template is a contributing factor to that, as the prominence of this template makes it a de facto source of "best practice".

Downsides to React.FC/React.FunctionComponent

Provides an implicit definition of children

Defining a component with React.FC causes it to implicitly take children (of type ReactNode). It means that all components accept children, even if they're not supposed to, allowing code like:

const App: React.FC = () => { /*... */ };
const Example = () => {
	<App><div>Unwanted children</div></App>
}

This isn't a run-time error, but it is a mistake and one that would be caught by Typescript if not for React.FC.

Doesn't support generics.

I can define a generic component like:

type GenericComponentProps<T> = {
   prop: T
   callback: (t: T) => void
}
const GenericComponent = <T>(props: GenericComponentProps<T>) => {/*...*/}

But it's not possible when using React.FC - there's no way to preserve the unresolved generic T in the type returned by React.FC.

const GenericComponent: React.FC</* ??? */> = <T>(props: GenericComponentProps<T>) => {/*...*/}
Makes "component as namespace pattern" more awkward.

It's a somewhat popular pattern to use a component as a namespace for related components (usually children):

<Select>
	<Select.Item />
</Select>

This is possible, but awkward, with React.FC:

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

but "just works" without React.FC:

const Select = (props: SelectProps) => {/* ... */}
Select.Item = (props: ItemProps) => { /*...*/ }
Doesn't work correctly with defaultProps

This is a fairly moot point as in both cases it's probably better to use ES6 default arguments, but...

type  ComponentProps = { name: string; }

const  Component = ({ name }: ComponentProps) => (<div>
	{name.toUpperCase()} /* Safe since name is required */
</div>);
Component.defaultProps = { name: "John" };

const  Example = () => (<Component />) /* Safe to omit since name has a default value */

This compiles correctly. Any approach with React.FC will be slightly wrong: either React.FC<{name: string}> will make the prop required by consumers, when it should be optional, or React.FC<{name?: string}> will cause name.toUpperCase() to be a type error. There's no way to replicate the "internally required, externally optional" behavior which is desired.

It's as long, or longer than the alternative: (especially longer if you use FunctionalComponent):

Not a huge point, but it isn't even shorter to use React.FC

const C1: React.FC<CProps> = (props) => { }
const C2 = (props: CProps) => {};

Benefits of React.FC

Provides an explicit return type

The only benefit I really see to React.FC (unless you think that implicit children is a good thing) is that it specifies the return type, which catches mistakes like:

const Component = () => {
   return undefined; // components aren't allowed to return undefined, just `null`
}

In practice, I think this is fine, as it'll be caught as soon as you try to use it:

const Example = () => <Component />; // Error here, due to Component returning the wrong thing

But even with explicit type annotations, React.FC still isn't saving very much boilerplate:

const Component1 = (props: ComponentProps): JSX.Element => { /*...*/ }
const Component2: FC<ComponentProps> = (props) => { /*...*/ }
Assignee
Assign to
Reviewers
Request review from
Time tracking
Source branch: github/fork/Retsam/patch-1