71

I always see examples of functional React components defined with arrow function syntax:

const foo = () => (...);

export default foo;

Rather than the more traditional function declaration syntax:

export default function foo() {
  return ...;
}

Is there a reason to prefer the former over the latter?

6
  • 68
    It looks a lot cooler Commented Mar 15, 2018 at 17:52
  • 3
    Citation needed :) I think this is opinion based, we just might be talking about personal preferences of developers. Commented Mar 15, 2018 at 17:53
  • 28
    This is one of the those bad practices that got popularased on the hype. React comunity started to do it, and everybody suddenly followed. Even though there are several reasons why it should be considered anti-pattern and normal function declaration should be preferred. Commented Mar 15, 2018 at 17:55
  • 9
    It's worth mentioning that the official React documentation uses the traditional function. Commented Jan 10, 2022 at 3:26
  • 4
    @DanMandel No it doesn't look cooler, it's annoying. Commented Feb 28, 2023 at 15:51

6 Answers 6

32

Actually, there is no difference between them, I made a little project on the CodeSandBox and made two simple components, one of them is the Arrow component by using the arrow function:

import React from 'react';

const MyArrowComponent = () => (
  <main>
    <h2>Arrow</h2>
  </main>
);

export default MyArrowComponent;

And the other is the Declaration component by using function declaration:

import React from "react";

function MyFunctionComponent() {
    return (
        <main>
            <h2>Declaration</h2>
        </main>
    );
}

export default MyFunctionComponent;

Then I ran the yarn build command and got the bundle like the one below:

(window.webpackJsonp = window.webpackJsonp || []).push([[0], {
  14: function (e, n, t) {
    "use strict";
    t.r(n);
    var a = t(0), r = t.n(a), l = t(2),
        c = t.n(l), u = t(3), i = t(4), o = t(6), m = t(5), E = t(7);
    var p = function () {
      return r.a.createElement("main", null, r.a.createElement("h2", null, "Declaration"))
    }, s = function () {
      return r.a.createElement("main", null, r.a.createElement("h2", null, "Arrow"))
    }, d = function (e) {
      function n() {
            return (
              Object(u.a)(this, n),
              Object(o.a)(this, Object(m.a)(n).apply(this, arguments))
      }
      return Object(E.a)(n, e), Object(i.a)(n, [{
        key: "render", value: function () {
          return r.a.createElement(
            'div',
            null,
            r.a.createElement('div', null, 'Hi'),
            r.a.createElement(p, null),
            r.a.createElement(s, null)
          );
        }
      }]), n
    }(r.a.Component);
    c.a.render(r.a.createElement(d, null), document.getElementById("root"))
  }, 8: function (e, n, t) {
    e.exports = t(14)
  }
}, [[8, 1, 2]]]);

Pay attention to the definition of the Arrow and the Declaration component:

var p = function () {
  return r.a.createElement("main", null, r.a.createElement("h2", null, "Declaration"))
}, s = function () {
  return r.a.createElement("main", null, r.a.createElement("h2", null, "Arrow"))
}

Both of them are defined in the same way, so there is no difference between them and it is fully opinion based on developers' attitude to code readability and clean code, based on ESLint 5.x in our team, we chose the arrow function to define the functional components.

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

3 Comments

Until you run into some issues, and spend a whole day trying to find the mistake just to realise you had to use normal functions instead of arrow ones. They are not the same, arrow functions break the jsx flow
@ErayZaxy how ? can you give some example of jsx flow breaking
How does yarn build know which function to translate as function and which to translate as arrow function? does it analyze the code for this usage?
31

I would say that this is a bit opinionated choice really. There are at least several reasons why I (personally) see arrow function use for a purely functional component as pretty bad practice. Here are those:

  1. Syntax abuse. When we define function component we don't need to pre-bind its context to a specific scope. The context (this) is going to be undefined anyway in the module namespace. The use of arrow functions is dictated here by pure aesthetics reasons like conciseness. But arrow functions as language feature has a very specific purpose for existence in the first place, and this is not coolness and conciseness.

  2. Error stack trace. Exceptions thrown in arrow function will be less descriptive because arrow function is anonymous by definition. This is not the huge problem probably since React project will most likely be configured with proper source maps support, but still stack trace will be a bit more clear if named function is used. As noted in comments this is not really an issue of the functional component, as the name will be the name of the variable basically.

  3. Less convenient logging. Consider this very typical pure function component style:

    const Header = ({ name, branding }) => (
      <header>
        ...
      </header>
    )
    

    In the function above it's impossible to throw in quick debugger statement or console.log. You will have to temporarily convert it to something like this

    const Header = function ({ name, branding }) { 
      console.log(name)
      return (
        <header>
          ...
        </header>
      )
    }
    

    This might be pretty annoying especially for bigger pure functional components.

That being said this is a very popular choice for many teams, also by default preferred by ESLint, so if you don't see the problem with it, then it is probably okay.

13 Comments

@dfsq 3 isn't much applicable, too. It's certainly more convenient to do this for multi-line function (temp variables help a lot), but you can always throw console.log(), or eval('debugger'), into one-line arrow (notice that React function components already have (...) for that. Also, it's not about regular vs arrow functions, it's about implicit vs explicit return. But important thing about debugging is that you have arguments in regular function (but arrow will have it in ES5 target, too).
3 isn't really a valid concern - you could just configure eslint to require explicit return and curly braces ``` const Header = ({ name, branding }) => { console.log(name); return ( <header> ... </header> ); } ```
@wmp224 oh, i see, we were talking about different things. of course you can. i was talking about short return notation.
@IvanKleshnin Component definitions do not need to be concise. If anything the preference should be clarity over brevity. Who is writing 1-2 line functional comps (at all) and then feeling good about it b/c of an implicit return? People do it b/c FB does it and ESLint defaults it. Try reading an ES6 module where everything is defined as a const...The distinction is valuable. Not to mention that the method signatures actually take more key strokes and are inherently busier because of the = and =>. Arrow notation is a feature and should be used with intention.
For #3 console logs, you can do => (console.log('msg'), <header>), or (<>{console.log('msg')}<header>
|
13

A few other points not mentioned in other answers:

  • With arrow function components, when using React dev tools in Chrome/Firefox, those components come up as Anonymous making debugging harder. These Anonymous components are also throughout dev tools including performance flame trees. Functional components display their name in dev tools.
  • A standard function declaration can be defined on a single line. You don't need to define the export default later in a file. This also makes it easier when you want to add/remove the default keyword.
export default async function MyComponent() {
  ...
}

1 Comment

By the latest ECMAScript standard, arrow functions will be given name on declaration if assigned to variable or property. For example, given const Button = () => {}, then Button.name === 'Button'. So react dev tool will not show them as Anonymous now.
11

Function declaration and arrow functions are different in their essence, but in the scope of your question, it's basically a code style preference. I personally prefer Function declaration as I find it easier to spot the meaning of that line of code.

If you will use Arrow functions or Function declarations, try to also think in terms of what makes more sense in the context. To make the code clean and easier to read it isn't only about the amount of code you write but what that code express.

I tend to use Arrow Functions for callbacks, for example, [].map(() => {})

1 Comment

You could argue that because ES6 implements so many arrow functions .map / .filter / .reduce, writing named functions would introduce inconsistency. A weak argument, an argument nonetheless.
2

Using Arrow function is way better than using a regular function not only because the syntax is clean and you will be able to write less code with the arrow function but also because of :

  1. Scope safety: when arrow functions are used consistently, everything is guaranteed to use the same thisObject as the root. If even a single standard function callback is mixed in with a bunch of arrow functions there's a chance the scope will become messed up.

  2. Compactness: Arrow functions are easier to read and write.

  3. Clarity: When almost everything is an arrow function, any regular function immediately sticks out for defining the scope. A developer can always look up the next-higher function statement to see what this object is.

For more details, you can take a look at these questions

When should I use Arrow functions in ECMAScript 6?

7 Comments

The this binding is irrelevant in functions that are not attached to objects.
To clarify - credit where credit is due: The 3 points listed above are from lyschoening's answer mentioned in the question link above.
I'm going to disagree that it's "easier to read". We read left to right, top to bottom and the usage of function makes it easy to identify declarations that are functions versus declarations that are variables without having to scan right visually. I absolutely hate reading files that are straight const down the left because it requires scanning right to determine if it's a function or a variable. Using the function keyword makes the code easier to scan top to bottom without scanning left to right.
"compactness" seems like the wrong term. export const Component = () => {…}; is actually longer than export function Component() {…}
1. This "safety" you are talking about, is only useful for methods (functions that are called from an object). Functional components are not dependent on an object, and the "this" keyword belongs to the scope of the module itself, which is going to be undefined or pointing to the window or document object (I'm not sure how React and Babel define it). 2. I think reading from left to right using the function word is actually easier to read. Arrow functions add an extra layer of syntax complexity. I will agree though that arrow functions are much much "cooler".
|
-1

There actually is an important difference, despite other previous answer. I recently faced a bug where choosing arrow functions over function declarations made the difference.

They differ in their handling of this. The linked mdn web docs page provides a good and details explanation, which I would recommend.

In summary:

Dynamic binding for function declarations

When a regular function (function declaration) is invoked on an object instance, this refers to that instance.

Lexical binding for arrow functions

Arrow functions inherit this from the parent scope where the function is defined and therefore to not point to the current caller object. The value of this also cannot be set bind, apply, or call methods.

This difference makes arrow functions especially useful for callback functions. While for regular functions, this typically refers to a global object in non-strict mode and to undefined in strict mode, arrow functions preserve the context of definition. In such a case, using arrow functions can be a good practice to avoid calling a function with undefined.

2 Comments

Yes, there are lots of differences between arrow functions and function declarations, but this question is about their use as React function components, where the this value is irrelevant
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review

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.