3

Is there a difference between arrow function and function notations?

The following two components appear to work the same.

One is implemented as React.FC<T> and the other as Function().

Is that just a different notation or are there other reasons for it?

AuthRoute.tsx as React.FC:

import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuthContext } from './AuthContext';
import RoutesUrls from './routesUrls';

export interface IAuthRouteProps {
  component: React.ComponentType;
  path: string;
  exact?: boolean;
}

const AuthRoute: React.FC<IAuthRouteProps> = (props) => {
  const { authorized } = useAuthContext();

  if (authorized) {
    return (
      <Route
        component={props.component}
        exact={props.exact}
        path={props.path}
      />
    );
  } else {
    return <Redirect to={RoutesUrls.LOGIN} />;
  }
};

export default AuthRoute;

AuthRoute.tsx as Function()

import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuthContext } from './AuthContext';
import RoutesUrls from './routesUrls';

export interface IAuthRouteProps {
  component: React.ComponentType;
  path: string;
  exact?: boolean;
}

export default function AuthRoute(props: IAuthRouteProps) {
  const { authorized } = useAuthContext();

  if (authorized) {
    return (
      <Route
        component={props.component}
        exact={props.exact}
        path={props.path}
      />
    );
  } else {
    return <Redirect to={RoutesUrls.LOGIN} />;
  }
}
1
  • When you use React.FC you get for example the children-prop in your props. This means that you have typings on props.children, which can sometimes be useful. Commented Dec 22, 2020 at 10:03

2 Answers 2

5

FC is functional component (as name implies)

Check the implementation of FC

type FC<P = {}> = FunctionComponent<P>;

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

It just has optional fields propTypes, contextTypes, which are typed, and will help when writing code

Personally, I prefer functional component when I don't need react prop validation, context or default prop declarations and also it's just shorter to write

type Prop = { name: string }

const Test0: FC<Prop> = (prop: Prop) => null
Test0.defaultProps = {
  name: 'Sam' // typed, with vs-code autocomplete
}

function Test1(prop: Prop) { return null }
Test1.defaultProps = {
  name: 'Sam' // untyped, no autocomplete
}

That's it.

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

Comments

4

Short answer:

If You want to go deeper with component types an improve TS compilation time (not code performance) - go with React.FC

If You prefer function notation, feel free to use it.

Anyway, compiled code will be the same.

Long answer

If You want to narrow component return types, You should use React.FC.

Let's start with simple example, partially taken from here:

import React from 'react'

type Props = {
   label: string
}

function A() {
   return <div>Child One</div>;
}

const B: React.FC<Props> = ({ label }) => {
   return <div>{label}</div>;
};

/**
 * Assume, You want to allow only children with particular props
 * In our case - props of children should match Props type
 */
function Parent({
   children
}: {
   children: React.ReactElement<{ label: string }>[];
}) {
   return (
      <div>{
         children.map((child) => (
            <div key={child.props.label}>{child}</div>
         ))
      }</div>
   )
}

function App() {
   /**
    * There is no TS error and it is almost expected ))
    */
   return <Parent children={[<A />, <B label={'hello'} />]} />;
}

So, we need to help TS to properly narrow App children types. Let's use native syntax for children instead JSX

// Please replace App in prev example with App2
function App2() {
    return React.createElement(Parent, {
        children: [
            /**
             * Here should be an error, because all
             * children of Parent should have Props
             * 
             * Why there is no error?
             * 
             * Because TS can't figure out proper type from A component annotation
             */
            React.createElement(A), 
            React.createElement(B, { label: 'd' })
        ]
    });
}

To make it work, we just need to replace:

function A() {
   return <div>Child One</div>;
}

with

const A: React.FC = () => {
   return <div></div>;
};

And voi-la, we have an error Property 'label' is missing in type '{}' but required in type '{ label: string; }'

Full working exmaple:

import React from 'react'

type Props = {
   label: string
}

const A: React.FC = () => {
   return <div></div>;
};


const B: React.FC<Props> = ({ label }) => {
   return <div>{label}</div>;
};

/**
 * Assume, You want to allow only children with particular props
 * In our case - props of children should match Props type
 */
function Parent({
   children
}: {
   children: React.ReactElement<{ label: string }>[];
}) {
   return (
      <div>{
         children.map((child) => (
            <div key={child.props.label}>{child}</div>
         ))
      }</div>
   )
}

function App2() {
    return React.createElement(Parent, {
        children: [
            /**
             * Here should be an error, because all
             * children of Parent should have Props
             * 
             * Why there is no error?
             * 
             * Because TS can't figure out proper type from A component annotation
             */
            React.createElement(A), 
            React.createElement(B, { label: 'd' })
        ]
    });
}

I see, that I did not convinced You. Ok, I understand. Let's take a look on next example. Assume, You want to return from one component some particular type of other component

import React from 'react'

function Two(){
   return <div></div>
}
type Props = {
  label: string;
}

type CustomReturn = React.ReactElement<Props>;


const MainButton: React.FC<Props> = (prop: Props): CustomReturn => 
    React.createElement(Two); // no error, but should be

There is no error at all, despite the fact that I have defined explicitly return type. Let's use React.FC:

import React from 'react'

const Two: React.FC = () => <div></div>

type Props = {
  label: string;
}

type CustomReturn = React.ReactElement<Props>;


const MainButton: React.FC<Props> = (prop: Props): CustomReturn => 
    React.createElement(Two); // Error, as it should be

With React.FC, TS was able to infer types.

2 Comments

Thank you for the very detailed explanation. If you go deeper, you can see differences. Are the arrow functions generally the recommended way? Should functions that have nothing to do with React but only be used for business logic also generally be implemented in arrow function notation?
@Alligator personally, I prefer arrow notation, but I think it is really up to you what type of function to use. No, you should not implement all functions as arrow, because they are different: hoisting, this etc...

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.