javascriptarrow-functions

Why is my arrow function with a braced body returning undefined?


This question is intended as canonical duplicate target for questions about problems that stem from confusing the single-line/expression arrow function body syntax with automatic return with their multi-line/block version.


I have an arrow function to add two numbers, but when I call it, it returns undefined. Why?

const add = (a, b) => {
  a + b
}

console.log(add(1, 2)) // expected: 3, actually: undefined

Alternative question:

My React component is supposed to render list items using map, but the list stays empty. Why?

<ul>
  {list.map(item => {
    <li>
      <a href="{item.url}">{item.name}</a>
    </li>
  })}
</ul>

Solution

  • Arrow functions support two different styles of bodies: expressions and blocks.

    If a single expression is provided (e.g. a + b) without braces { } around it, that expression is automatically returned:

    const add = (a, b) => a + b
    

    If a block enclosed by { } is provided, it works like a regular function body and requires a dedicated return statement to return a value:

    const add = (a, b) => {
      return a + b
    }
    

    Single-expression bodies are often used to write simple functions in a concise way which execute one operation or condition, as in the following examples:

    if (users.every(user => user.age >= 18)) { /* ... */ }
    
    const emails = users.map(user => user.email)
    
    const titleCased = string.replace(/\b\w/g, s => s.toUpperCase())
    
    // in the following example, the return value is irrelevant
    setTimeout(() => doStuff(1, 2, 3), 1000)
    

    In most other cases, especially if you want to have multiple statements, loops or conditions in your function body, a block is used.

    Note that you can have a function body spanning multiple lines even without a block if it is still a single expression, but if you would like to turn it into a block for readability reasons, you must not forget to add return (unless your function isn't supposed to return anything).

    Now, this is the reason why your add function is returning undefined - it neither has a single-expression body (the { } make it a block) nor does it have any return statement in its body. So, what happens is that a + b is evaluated, but the result isn't used for anything - it is thrown away and execution continues, reaching the end of the function and returning without any return value since none was given, i.e. returning undefined.


    In the React case, the problem is the same. You are embedding the return value of a .map call, which should be an array of further content to render, but because your callback is not returning any value, you are mapping the items to several undefined values and rendering that at the end.

    There is another twist here though: you may often need multiple lines in the element(s) that you return from a function like a map callback, but you will find that neither of the following two options looks quite clean:

    <ul>
      {list.map(item => <li>
        <a href="{item.url}">{item.name}</a>
      </li>)}
    </ul>
    
    <ul>
      {list.map(item => {
        return <li>
          <a href="{item.url}">{item.name}</a>
        </li>
      })}
    </ul>
    

    Instead, what is usually done is enclosing the expression in parentheses ( ). It is a still a single expression at the end, avoiding the need for an extra return statement, but is a lot nicer to work with:

    <ul>
      {list.map(item => (
        <li>
          <a href="{item.url}">{item.name}</a>
        </li>
      ))}
    </ul>
    

    For more information about arrow functions in general, see here. To read about other differences between arrow functions and regular functions (such as different behavior of this), see here.