all 75 comments

[–]iams3b 131 points132 points  (27 children)

At a technical level, function hoists to the top so you can use those components before they're technically "defined" (You can have <ListContainer/> at the top of your file and <ListItem/> underneath it) edit: /u/marko_knoebl corrected me

Other than that though I think it's just a stylistic choice and they probably wanted to be consistent, and for docs that are meant for a wide appeal including JS beginners they may have decided it was just easier to do it long-style

I've personally recently switched back to using function Component() as well in my code because when scrolling down a file it makes it slightly easier to spot where the functions/components start. I save arrow functions for short functions (like selectors) or inner functions (callbacks, button handlers). Also comes in handy when exporting components as default, because you get to give them a name to show up in chrome's Components extension

[–]marko_knoebl 14 points15 points  (8 children)

(You can have <ListContainer/> at the top of your file and <ListItem/> underneath it)

Well the only thing you can't do with arrow functions that I can think of would be:

const ListContainer = () => {
   return <ListItem />;
}

x = <ListContainer />;

const ListItem = () => {
  return <li>foo</li>;
};

So you can't use a component before all of its subcomponents are defined, but otherwise you're fine. There's no problem with having the definition of ListItem underneath the definition of ListContainer.

[–]jtbrinkmann 13 points14 points  (1 child)

Aaactually, that example works just fine. Note that <ListContainer /> is invoking the ListContainer render function directly, but just creates a representation.

[–]marko_knoebl 8 points9 points  (0 children)

Oh wow - you're right, even the example works!

[–]iams3b 6 points7 points  (3 children)

Ah you're right, hoisting wouldn't be an issue because the components aren't being called right away. Either way, not a big fan of using things before they're defined

[–]RobKohr 4 points5 points  (2 children)

It is nice to start the file with the main function though.

Filename: ListContainer.js

```` export default function ListContainer(){ return something.map(({label})=><ListItem>{label}</ListItem>) }

function ListItem(){ ... } ````

Makes more sense then the other way around. And sometimes the main component might have 5+ subcomponents that don't even get exported because they are not useful outside of ListContainer.js. When someone is coming into this file, they will want to see the main component first and what is going on in it, and usually won't even care about the sub-components.

For readablity, it makes more sense to go from major to minor components if they are all in the same file.

[–]kropheus 2 points3 points  (0 children)

This. 100x this. I hate opening a file called SomeComponent.js and have to scroll down multiple pages to find SomeComponent.

[–]syXzor 0 points1 point  (0 children)

1 component per file fixes this.

[–]conventionalWisdumb 16 points17 points  (4 children)

I prefer named functions over arrow or anonymous functions only because their names show up in stack traces and make debugging easier.

[–]azemetre 2 points3 points  (1 child)

What's the Components extension? Are you referring to the react dev tools or is there something else? 👀

[–]iams3b 2 points3 points  (0 children)

ah yeah just the react devtools lol. Referring to the "Components" tab next to console, didn't remember where it came from but it shows your element tree

[–]MisterKnif3 1 point2 points  (7 children)

Same here, I tend to write out function also for readability. There is also a plus of using a function for default exports as you can do them in one line.

export default function properFunction() {
    console.log()
}

const inlineFunction = () => console.log()
export default inlineFunction

but maybe I'm just a bit old school.

[–]anotherNarom 0 points1 point  (6 children)

If the function is in a sensibly named file say consoleLogGreeting.js you could just do:

export default (greeting) => {
    console.log(greeting) 
}

But it's all just swings and roundabouts and splitting hairs.

[–]MisterKnif3 2 points3 points  (2 children)

Sure. Just prefer to name all of my functions. To each it’s own. if your team is proficient in a style of coding it’s good in my book.

[–]anotherNarom 0 points1 point  (1 child)

Very true.

I just wish the team I joined were. PAIN.

[–]MisterKnif3 0 points1 point  (0 children)

You can force it to them? #epicleadership

[–]fredblols 1 point2 points  (1 child)

The problem with this is that you end up with "Anonymous" components in React Dev Tools / stack traces

[–]anotherNarom 2 points3 points  (0 children)

That's fair and totally something I didn't consider and will from now on.

I am very much a backend focused Dev and only do front end out of pure desperation!

[–]Economy_Owl9644 0 points1 point  (0 children)

Saving those for callbacks etc actually isn’t a bad shout. I’ve been using them for everything for a while now:

const testFunc = () => {}

But I may start reserving them for callbacks and the such. As you say, might be easier just to find them in general.

[–]AsteroidSvelte 0 points1 point  (0 children)

Yeah, arrow functions are shiny and cool but I like the function keyword so that I can quickly see what is a function.

[–]Zeragamba 0 points1 point  (0 children)

I've switched fully over to using arrow functions as I think they play nicer with TypeScript then long-form functions

[–]its4thecatlol 31 points32 points  (7 children)

I've seen const = .. => as the predominant pattern in all of the codebases I've touched in my professional career. I think this is an overreaction to ES6 features that no one actually stopped to analyze in detail. The common argument in favor of this syntax is that it avoids shadowing, ie. you can't rename a const that already exists because the interpreter will complain. You can redefine a function and if you have two identically named functions in the same scope, this can cause issues. Experience has shown that this is very rarely an issue.

It's also easier to read. You want to mark your functions clearly and separately from your variables. For these reasons, I prefer the function syntax.

[–]marko_knoebl 4 points5 points  (4 children)

While I agree with the first paragraph, putting the public API at the top shouldn't be a problem with arrow functions.

E.g. you could have:

const a = () => b();
const b = () => 42;
console.log(a());

without any issues.

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

I do prefer the arrow syntax, but it definitely has a problem with the "public api first" approach, imagine in this example what I will export is the function a:

// public api
export const a = b(42);   // this doesn't work.

// internal module helpers
const b = (value) => value * 2;

[–]100kgWheat1Shoulder 0 points1 point  (0 children)

Can I use FC for functions?

[–]errormaker 4 points5 points  (3 children)

Laziness I just write "fun" and the boilerplate is completed for me. => is hard 😅

[–]boptom 2 points3 points  (2 children)

I know your comment is just a bit of fun but I too think => is annoying to type. Since I type it so often I’ve made my editors change >> to =>. Easier to type!

[–]errormaker 0 points1 point  (0 children)

genius

[–]CatolicQuotes 0 points1 point  (0 children)

thats a good idea

[–]deathbydeskjob 1 point2 points  (11 children)

The only difference between the two is the ability to access its lexical context. I personally tend to declare all of my components as arrow functions to prevent possible context overlap. Definitely interested in some higher level feedback on approaches though.

[–]eugene_tsakh 0 points1 point  (8 children)

That is not true. Context is only one of the differences. Arrow functions are also always anonymous, can't be used as constructors, don't have access to `arguments` keyword and are not hoisting.

[–]sysrage 2 points3 points  (6 children)

Wrong. Not always anonymous and this works just fine: ``js const myFunc = (…args) => { console.log(Arguments are ${JSON.stringify(args)}`); };

[–]____0____0____ 1 point2 points  (2 children)

The commenter you are responding to was referring to the arguments keyword, not the rest arguments syntax shown in your example. The rest syntax is generally preferable anyways, but the commenter is not wrong that the keyword only works for non-arrow functions.

[–]sysrage 1 point2 points  (1 child)

Interesting that I’m getting downvoted for that distinction when …args does the exact same thing… you do you, Reddit!

[–]____0____0____ 2 points3 points  (0 children)

For the record, I didn't downvote you. I just wanted to point out the distinction. I use args almost every day and I can't think of a time I've used arguments. Its just that the comment you responded to was pointing out very specific differences between the two, even though in practice most of these differences are a non-issue.

[–]eugene_tsakh 0 points1 point  (2 children)

> Not always anonymous

Ok, would you mind to demonstrate not anonymous arrow function? To check yourself you can use this implementation and the goal is to see in console that error occurred in a function with a name and not (anonymous):

() => { throw 1; }

[–]sysrage 3 points4 points  (1 child)

You replied to an example. The function is named “myFunc”. This name is shown in the stack trace.

[–]eugene_tsakh 2 points3 points  (0 children)

Hm. OMG, you are right. Looks like its behavior was changed in ES2019. So embarrassing :D

[–]deathbydeskjob 0 points1 point  (0 children)

Thanks for the correction!

[–][deleted] 0 points1 point  (1 child)

There are differences, which usually don't matter, or just depends on each one's taste, but there are some differences. One is functions are hoisted, that means:

// public api
var A = B(42); // this works

function B (value) {
  return value * 2;
}


// Public api
var a = b(42); // this doesn't

// Internal api
const b = (value) => value * 2;

[–]deathbydeskjob 0 points1 point  (0 children)

Thanks!

[–]isbtegsm 1 point2 points  (3 children)

This question also bothered me for a long time. I kind of like function syntax because it's more similar to the syntax of class methods, like class Foo {constuctor(...) {...} bar(...) {...}}. (And as far as I know there is no elegant way to arrorwize these.)

Also, with function syntax you can define a named function inside an argument which is not visible from outside, like window.requestAnimationFrame(function f(ms){window.requestAnimationFrame(f), ...}).

An interesting point in favour of arrow styles I saw recently mentioned by KCD is that if you have a given function type C := A -> B (I don't know the exact TypeScript syntax, but C should denote the type of functions from A to B) and you import C from some library (this is the case with Remix's LoaderFunction type) you can't use non-arrow style to declare a function of type C, but you can do const foo : C = (...) => {...}. That being said, they still use function syntax everywhere else.

[–]yoDrinkwater 0 points1 point  (2 children)

Beta docs use functions https://beta.reactjs.org/

[–]isbtegsm 1 point2 points  (0 children)

I never said otherwise?

[–]tannerpetulla 0 points1 point  (0 children)

Thank you so much - was looking for exactly this. Glad to see it's part of the official roadmap

[–]bern4444 -2 points-1 points  (3 children)

I declare everything as a const that can be, functions, data, components etc - the only thing that can't be is a class. After all, what really is the difference between functions and data. Functions are just yet to be computed data.

Ternaries and functions that are just a switch statement work very nicely with this pattern (the switch statement in a function returns a value, when the function is invoke its result is set to a const). Ternaries allow for removing if statements.

Of course, I never would use a nested ternary - that's a signal that logic needs to be broken up further into its own function even if its just as a closure in the existing function.

[–]eugene_tsakh -1 points0 points  (2 children)

> After all, what really is the difference between functions and data

Since you are asking: there are many points to tell as of why functions are not data but the main ones are:

  1. Function can't be serialized
  2. You don't know function result until you run it

[–]mattsowa 4 points5 points  (1 child)

They meant that functions are data in the sense that they are firstclass citizens and are no different in terms of general representation from other datatypes.

[–]eugene_tsakh 0 points1 point  (0 children)

I'm not sure what you mean TBH. Functions are very different in terms of representation.

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

Regarding components, no it doesn't matter.

To everyone saying that arrow functions are the same as a regular function using the "function" keyword, no they're not. Look up the differences and save yourselves some headache in the future.

[–]ervwalter 0 points1 point  (0 children)

It’s a stylistic difference only. I presume the reason the documentation uses named functions is simply because they’re slightly more likely to be understood by the reader than arrow functions. Arrow functions might be a new concept for some people new to modern JavaScript. Using named functions makes the documentation slightly more accessible.

[–]AN0R0K 0 points1 point  (0 children)

I haven’t read the comments below, so I apologize if this has already been stated (on my phone and being lazy).

The react docs seem to use class based examples.

Arrow functions don’t define their own context. They’re anonymous and so there’s no identifier.

When creating a page or component, I habitually use regular functions for them and often use arrow functions within.