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} />;
}
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With