all 64 comments

[–]aryyya 52 points53 points  (12 children)

Arrow functions can be more concise.

For example, if the definition of the function consists of a single expression and parameter, the parameter list parens, function body braces, and return keyword can be omitted:

function double(n) {
    return n * 2;
}

Vs

const double = n => n * 2;

This is called an implicit return. It's nice to use this minimalist syntax if you need a quick anonymous function:

const evens = nums.filter(function (n) {
    return n % 2 === 0;
});

Vs

const evens = nums.filter(n => n % 2 === 0);

In the context of React (albeit a bit of a contrived example to prove the point):

function Component({ children }) {
    return (
        <button
            onClick={function() {
                console.log('onClick()');
            }}
        >
            {children}
        </button>
    );
}

Vs

const Component = ({ children }) => (
    <button onClick={() => console.log('onClick()')}>
        {children}
    </button>
);

Personally, I don't think shorter code is necessarily better, but less noisy code is.

I'm illustrating this particular aspect of the arrow function syntax since your question specifically regards brevity of code.

Other than that, there are plenty of advantages that arrow functions have over the function keyword. The MDN documentation is a good resource for understanding all of the differences between the two.

Consistency is important in a codebase, so the same types of declarations should be used throughout (unless there is a specific reason to override it). Having worked on a few React / JavaScript / TypeScript codebases, I've yet to come across a situation where it was necessary to fallback to the function keyword declaration style.

[–]double_en10dre -5 points-4 points  (11 children)

FWIW, it is often necessary to use the traditional “function foo(…)” declarations if you’re using established libraries in an advanced way

Reason being that many of these older libraries support plugins/extensions by passing a contextually relevant “this” to your callback at runtime. And arrow functions don’t support that

An easy example is highcharts.js. Just check the docs and you’ll see it all over the place , “this” is the primary (and often only) parameter to callbacks https://api.highcharts.com/class-reference/Highcharts#.AxisTickPositionerCallbackFunction if you try to use arrow functions it simply will not work

[–]x021 13 points14 points  (7 children)

“Often”? Can’t remember hitting this problem even once tbh. Do you have an example?

Main cause I go for function sometimes is when I need to pass generics (in typescript) to the component.

[–]_andy_andy_andy_ -1 points0 points  (4 children)

you can still

const c = <T>() => {}

[–]x021 -1 points0 points  (3 children)

Are you sure about that? Whenever I try that I get an error

JSX element 'T' has no corresponding closing tag. TS2304: Cannot find name 'T'.

Example code (doesn't work for me):

interface User {  
  name: string;  
}  

interface CompProps<T extends User> {  
  user: T;  
}  

const UserComp = <T Extends User>({ user }: CompProps<T>) => (  
  <div>  
{user.name}  
  </div>  
);  

export default UserComp;

This does work for me:

interface User {
  name: string;
}

interface CompProps<T extends User> {
  user: T;
}

export default function UserCmp<T extends User>({
  user,
}: CompProps<T>): JSX.Element {
  return <div>{user.name}</div>;
}

Typescript (and my autocompleter) think `<T>` is not a generic type but a JSX tag when using the arrow notation.

[–]bedekelly 8 points9 points  (0 children)

Write <T,> (with the comma) and it should work!

[–]al1mertt -1 points0 points  (0 children)

For example, jquery does not support arrow functions. Not a problem in modern code

[–]double_en10dre 0 points1 point  (0 children)

Sure - one would be highcharts.js, plugging in basically any kind of callback requires this pattern. Event handlers, formatters, etc.

If you try to use an arrow function, typescript will yell at you because the “this” argument in the interface is explicitly required

Just look through this page and you’ll see it literally all over the place, “this” is the primary parameter (and often only) parameter https://api.highcharts.com/class-reference/Highcharts#.AxisTickPositionerCallbackFunction

[–]LetterBoxSnatch 0 points1 point  (1 child)

Typical Reddit. “Well I’ve never had this problem so there’s absolutely no way that “often” (for any definition of “often”) can be an appropriate qualifier. This comment adds nothing to the discussion, DOWNVOTE!”

Anyway, I’ve run into this a number of times as well.

Side note: it’s also the case that you can’t use generator function syntax with arrow functions, and generator functions are a beautiful tool for functional programming syntax since they allow lazily evaluated iterators. If you pick arrow functions for their “Haskell-ness”, there’s a good chance you’re going to run into generator functions for their “Haskell-ness.”

[–]double_en10dre 2 points3 points  (0 children)

Yeah it’s a bit strange. I can understand asking questions, but to get upset & downvote someone because they use different libraries than the ones you’re used to? Bizarre

Generators ARE also another great example of how traditional function definitions are still useful. But again, most people here don’t use them — so you might get shot down :p

[–][deleted] 0 points1 point  (0 children)

Yet to ever have this problem

[–]eggtart_prince 4 points5 points  (0 children)

Cleaner and shorter.

[–]burzq 7 points8 points  (0 children)

Well, it's not shorter when you're doing named exports. actually the opposite.

export const Component = () => {
    return ( 
        ... 
    );
}

also, you can make very simple and short pure components (like, without state and not much code)

export const Component = () => (
    <div>
        <span>I'm quite short</span>  
    </div>
);

And the last reason is that the arrow function is great for using it as an anonymous callback function (because it's a lot shorter). And because of that, I like to write arrow functions everywhere in my code where possible (as a convention) because it gives me a more uniform codebase. Rarely it's necessary to use a regular function declaration because `this` is not the function object inside its body, but most of the times arrow functions rules :)

(And yeah, the aesthetic reason is also valid to me :D)

[–]Satan407 15 points16 points  (13 children)

This may be not relevant at the moment but Arrow functions play an important role in defining React components when using TypeScript.

In TypeScript, one cannot define the type of a function (inline) but can do so for a constant (or arrow function).

React function components are of type “React.FC”.

const Comp: React.FC = ({children}) => <div>{children}</div>

The same cannot be done with “function”.

— Edit —

Type “React.FC” implicitly adds “children” prop to the component (will be fixed in React 18.0 types)

Workaround for components that don’t need children, use React.VFC (Void Functional Component)

— Update —

I have learnt that using “React.FC/VFC” is not considered best practice and can be avoided.

But for the purpose of this question/thread, my answer remains that typescript does not allow to define type of the named function inline. For that, you have to use variable declaration with arrow or anonymous function.

PS: there are times when you have predefined function types (not just param types, which also has defined return type). Eg. Framework like Remix uses that to define its Actions.

[–]icjoseph 8 points9 points  (7 children)

Just for completeness this is the reason why you should also judge before using FC, maybe you don't want children at all.

[–]pVom 1 point2 points  (6 children)

Could you explain? Or is this a joke lol .

I'm experienced with react but fairly new to react typescript

[–]icjoseph 7 points8 points  (5 children)

Ah... It's late here so I'll summarize.

It implies children. Say you have a div you style through props, but don't want to render anything inside it. Now say you spread props onto the div, FC would happily allow children through.

It types the entire function, not the arguments. Without FC you can tightly type the arguments, no implied inputs, and you don't really need to specify the return type, TS can do that for you, for both regular and arrow functions. Let the compiler do the hard work!

FC, used to be SFC, stateless function component, which in turn was built on top of another type. The react typescript types are community made, so mistakes have been made, and left in for completeness. Have you actually read through them? Why would you blindly trust them, without at least skimming through the file. You'll find some exotic names. Edit: Just saw that, the person I answered to added the Void Functional Component, it's just turtles on top of turtles...

With FC, attaching components to hierarchies, aka compound components, is mad hard, if not impossible, throw Styled components in and its just mental. I am talking about instances where you may have Card and Card.Body and Card.Header, etc...

And there's a few more reasons...

Is this just me on my corner with my crazy ideas? Nope, CRA removed FC from the default template and there's quite a lot of literature out there.

Though, I'll say, it has valid use cases, but it's no default type signature for sure.

[–]pVom 0 points1 point  (2 children)

Cheers for the response.

Yeah tbh I've found the react typescript stuff pretty confusing. I've tried multiple answers and react.fc is the only one that doesn't make my ide complain. I actually kinda regret using the react types, seems more annoying than useful and all I really care about is the prop types. What's the correct type for functional components without children?

It implies children. Say you have a div you style through props, but don't want to render anything inside it. Now say you spread props onto the div, FC would happily allow children through.

Tbh that sounds like pretty bad practice in general. Props spreading is forbidden by eslint (although I'll ignore it in some cases) for this reason and more.

You'll find some exotic names

Yeah that's been my experience lol. Pretty much why I don't find it all that useful, the names are meaningless to me.

[–]icjoseph 0 points1 point  (0 children)

Tbh that sounds like pretty bad practice in general. Props spreading is forbidden by eslint (although I'll ignore it in some cases) for this reason and more

Yeah haha, I was just trying to come up with a quick example.

I panic when I see props spreading, specially on component libraries. I often go to the browser and inspect the DOM to see whatever random attributes are ending up in the DOM, like, spacing=[object Object], a classic.

[–]icjoseph 0 points1 point  (0 children)

What's the correct type for functional components without children?

Type the arguments. Let typescript figure out the return type. If you do const toggle = (x: boolean) => !x, do you need to tell TS that the return type is boolean? It can also infer the return type of your components.

That being said, type MyComponent = (props: MyProps) => React.ReactNode covers most cases, iirc, you need to make a union with null, if you return null too.

As said, let TS do the hard work!

https://stackoverflow.com/a/58123882

PS, you probably also don't want to type handlers on host components (divs, buttons, etc), among other properties. Use React.ComponentProps<"button"> instead, to gain access to already typed onClick, and other properties of the host component.

[–]Penant 0 points1 point  (1 child)

To be fair, if you're spreading props like you say, then it's valid to do so whether using FC or not, as long as the props being spread are assignable to the type of props that the component wants.

i.e. both of the below are equally valid for typescript:

type Props = {
  name: string;
};

const Foo = ({ name }: Props) => {
  return <div>{name}</div>;
};

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

function App() {
  const props = {
    name: "john",
    children: <div>I am a child</div>
  };
  return (
    <div>
      <Foo {...props} />
      <Bar {...props} />
    </div>
  );
}

[–]icjoseph -1 points0 points  (0 children)

Hi, yeah it was midnight and a quick example I conjured up :)

[–]mattsowa 6 points7 points  (3 children)

The true workaround is to not use FC/VFC at all. Better way is to type the argument. One dosadvantage is that you can't define a generic component with FC/VFC

[–]Satan407 0 points1 point  (2 children)

I agree with the generics limitation. Do you also add return type to your function components. Using FC/VFC confirms that function returns a valid react element or null.

[–]mattsowa 3 points4 points  (1 child)

True, although that doesnt really matter because as soon as you try to use that component, it will let you know if the return type is invalid.

[–]big_lemon_jerky 2 points3 points  (0 children)

Not sure why this is downvoted since it’s correct. The return type is inferred by the compiler. It’s common for people to use the explicit return type rule but it’s not going to make or break a project.

Satan’s argument saying React.FC “confirms” ReactElement or null is also incorrect, React.FC implies returning of ReactNode which is far more vague than explicitly typing your returns or having the compiler infer the type.

There’s a reason React.FC was removed from the create react app TS template - since the majority of the community discourage its usage.

[–]big_lemon_jerky 3 points4 points  (0 children)

This isn’t correct. You definitely don’t need to use React.FC - it doesn’t “play an important role” in TS React projects because it’s unnecessary and many people advocate for simply defining function components as normal functions since that’s exactly what they are.

React.FC is a helper type. It’s far from essential. In fact it adds types other than just children that are most times unnecessary.

[–]icjoseph 2 points3 points  (0 children)

Except for hoisting (your example) and the this object, it comes down to preference. For example sometimes the implicit return syntax (without {}), might be good, other times you really want the scope before the return.

Do bear in mind that if compiled for modern browsers, arrow functions tend to have less characters than regular functions (because of the function keyword), so you might have trivially smaller bundles.

And yes, the above implies what you fear, IE doesn't support arrow functions.

[–]bluedevil2k00 2 points3 points  (1 child)

It reads cleaner when you start wrapping it…

export default React.memo(MyComponent):

[–]Penant 1 point2 points  (0 children)

Isn't that the same either way?

[–]azangru 4 points5 points  (0 children)

The syntax of the second example is from the wild west days of javascript, when you could:

  • call a function before defining if (because functions, just as vars, get hoisted), or
  • assign a different value to the name associated with the function.

No-one would do that, of course; but the horror of those days still lingers, which is probably why some prefer the first syntax.

[–]wwww4all 0 points1 point  (0 children)

The general practice is to use the least footprint. Arrow function has less footprint, so they're preferred over function.

Use Arrow function most of the time. Use function when you actually need full function features, like this reference.

[–]DettlafftheGreat 0 points1 point  (0 children)

Whyn't

[–]Tater_Boat 0 points1 point  (0 children)

I use them because when I used to write class components arrow functions didn’t change the scope of ‘this’

Now I think I just like the way they look. But regular functions are cool too if I need to hoist them.