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>
{item.name}
</li>
})}
</ul>
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>
{item.name}
</li>)}
</ul>
<ul>
{list.map(item => {
return <li>
{item.name}
</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>
{item.name}
</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.
Arrow functions return code in these cases:
Case 1: When it's written inline like below
/* notice that this is an implicit return
and we don't need a return statement here as the code is
in the same line after the => */
const add = (a, b) => a + b
console.log(add(1, 2)) // expected: 3
Case 2: When it's written with round brackets () like this
Case 2 - example 1
/* notice the round bracket here.
You will use this when you have a
block of code here unlike case 1
where you had a single line to return */
const add = (a, b) => ( // this is round bracket, no return needed
a + b
// and other blocks of code. Look at the below JSX example for
// for this case
)
console.log(add(1, 2)) // expected: 3
The above example is similar to first case 2 - example 1, but this case is more suitable for a single block of code mostly for JSX like below
Case 2 - example 2
<ul>
{list.map(item => ( // this is round bracket, no return needed
<li>
{item.name}
</li>
)}
</ul>
Case 3: With an explicit return statement when you use curly braces like this
const add3 = (a, b) => { // curly braces + return statement
return a + b;
};
const res3 = add3(1, 2);
console.log(res3); // 3
In your case, notice you're mixing both cases 2 and 3. Meaning, you are using curly braces as defined in case 3 and also not using return keyword then like in case 2 which is the reason it doesn't work.
Code : https://codesandbox.io/s/javascript-forked-ckjg69?file=/src/index.js
Related
Sorry if my English bothers you but I've just come to realize you do not need to use ( ) with the function while using conditional - at least that's the case with ternary opeartor.
Here's my code:
let mark = {
fullName: 'Mark Wough',
mass: 100,
height: 1.8,
calcBMI: function()
return this.mass / (this.height * this.height;)
}
};
let john = {
fullName: 'John Smith',
mass: 90,
height: 1.5,
calcBMI: function()
return this.mass / (this.height * this.height;)
}
};
console.log(mark.calcBMI(, john.calcBMI());)
mark.calcBMI>john.calcBMI? console.log('Mark has higher BMI'): console.log('John has higher BMI');)
Notice I'm doing mark.calcBMI instead of mark.calcBMI() and both seem to be working!
Can any one please explain if that's exactly the way it is or I'm missing out on something and if its correct that you dont need to use ( ) in functions while using conditional, is this a good practice not to use them just because its optional or I should use them to be on the safer side?
In your code mark.calcBMI and john.calcBMI are both references to functions. So when you perform:
mark.calcBMI > john.calcBMI
you're checking if one function is larger than another (note that these are function values, not the returned value from calling these functions). So the above is more or less analogous to writing:
(function() {}) > (function() {})
As it doesn't make sense to ask if one function value is larger than another, JavaScript will try to convert both functions to primitives (primitives being types such as strings, numbers, booleans, etc). As functions are objects, JavaScript will call the .toString() method both of the functions, which will convert the function objects to primitive strings. JavaScript does this conversion for us, so behind the scenes it gets interpreted as:
"function() {}" > "function() {}"
Now that both functions are strings, they can be compared. Since the left-hand side string is not larger than the right-hand side string (they're equal), you get false as a result. The same idea applies for your calcBMI functions for both john and mark. As their .toString() values are both the same you'll get false when comparing the two functions.
This means that you need to use parenthesis ( ) when calling your functions1, otherwise:
mark.calcBMI > john.calcBMI
... will always evaluate to false given that both calcBMI toString methods return the same string.
See example below for further understanding:
const ex1 = (function() {}) > (function() {});
console.log("ex1", ex1);
const ex2 = (function(a) {}) > (function(b) {});
console.log("ex2", ex2);
const ex3 = (function(b) {}) > (function(a) {});
// "function(b) {}" > "function(a) {}", code unit of "b" larger is than the code unit of "a" so the result is `true`
console.log("ex3", ex3);
1. the exception to this is if your methods are getters
I'm working in React and I'm currently getting the error "Expected to return a value at the end of arrow function array-callback-return". I've searched other questions, but can't seem to find one that matches min (unless I'm totally blind). I have the following code.
const self = this;
const relevantCompanyMeasures = this.props.companyMeasures
.filter(companyMeasure => {
for(const measure of self.state.measures) {
if(measure.id === companyMeasure.measure_type_id) return true;
else return false;
}
});
The code loops through two arrays of objects looking for a match between the two. If I had only returned true for matches, I would have understood the error. However, I return false in the case that there is no match. As a result, I'm struggling to see how I'd end up in a situation where there is no return value at the end of the arrow function.
That is because the return only exits the for loop, and not the entire callback in Array.prototype.filter(). Instead of using for loop to check if companyMeasure.measure_type_id exists in self.state.measures, you can simply do an array map, which returns all the IDs, and then check against that return array if the ID exists using Array.prototype.includes:
const self = this;
const relevantCompanyMeasures = this.props.companyMeasures
.filter(companyMeasure => {
return self.state.measures.map(m => m.id).includes(companyMeasure.measure_type_id);
});
Even better:
since you are using arrow functions, the lexical this from the enclosing scope is preserved, so you don't even need to proxy this at all
you can also create the array map outside the .filter() callback to reduce overhead
now the .filter() callback is a one-liner, we can get rid of the curly brackets and take advantage of implicit returns in arrow functions
Here's how the final code should look like:
const ids = this.state.measures.map(m => m.id);
const relevantCompanyMeasures = this.props.companyMeasures
.filter(companyMeasure => id.includes(companyMeasure.measure_type_id));
Either:
The loop starts
You get the first measure
The condition is true and you return true
You never advance to the second measure
Or:
The loop starts
You get the first measure
The condition is false and you return false
You never advance to the second measure
Get rid of the else and move the return false to after the loop so it only runs if you get to the end of the loop without finding a match.
const self = this;
const relevantCompanyMeasures = this.props.companyMeasures
.filter(companyMeasure => {
return self.state.measures.map(meas => meas.id).indexOf(companyMeasure.measure_type_id)!=-1
}
);
Try this
I'm trying to map through collection of objects inside an object and access the color item, but i get an error Unexpected token, expected ",". This is how i'm trying to map through. Is this the right way to map objects to retrieve value from colors.
{Object.keys(this.state.lists).map((item, i) =>
(this.state.lists[item].colors).map(item, i) =>
<li key={i}>{this.state.lists[item].colors[item]} </li>
)}
this.state.lists looks like this:
{{id: 1, colors:["red", "blue"]}, {id: 2, colors:["green", "yellow"]}}
You are not passing a callback function to your second map call, .map(item, i). Hence the syntax error. It should instead be something like .map((item, i) => ...).
Here's some cleaned up code that might make sense of this, though I haven't tested if it works with React:
const colors = Object.keys(this.state.lists).map(itemKey => {
return <li key={itemKey}>{this.state.lists[itemKey].colors[0]}</li>
})
And when you render,
<ul>{colors}</ul>
When using ES6 functions, you can omit the () of the parameters, only if you use 1 parameter. What you've done is actually closed your map before you even got to the fat arrow (=>). Your error is saying it doesn't understand the , in map(item, i), since map doesn't accept a second parameter. Here's a bit of a break-down, followed by some optimized code for your problem.
A basic ES6 function is () => {}, where the parameters go between the () braces, and the code goes between the {}.
Here's a basic sum function: (a, b) => { return a+b }. Since this only has one line, and it's the return value, you can omit the {} brackets. i.e., (a, b) => a+b
Here's a hello function: (name) => { return 'hello ' + name }. Since it only has 1 parameter, you can use name => { return 'hello ' + name }. Or even using the above rule: name => 'hello ' + name.
These shortcuts can make code easier to write, but perhaps more difficult to understand. If in doubt, just always keep the () braces to avoid confusion.
const obj = {
1: {id: 1, colors:["red", "blue"]},
2: {id: 2, colors:["green", "yellow"]}
}
for (key in obj) {
const item = obj[key];
item.colors.map((color, i) => {
console.log( `<li key=${item.id}-${i}>${color}</li>`)
// Below lines are commented out because StackOverflow
// does not process JSX tags. Just uncomment and remove
// the console.log above
// return (
// <li key={item.id}-${i}>{color}</li>
// )
});
}
NOTES: Instead of using Object.keys to get an array of keys, I just use a for...in loop to accomplish the same thing.
Documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
This question already has answers here:
Arrow function without curly braces
(9 answers)
Closed 4 years ago.
Learning React and trying to cheat off this codepen. I do not understand 2 things about the map function in FormCard.
Why does this .map function have a return statement, I did not see a return on other examples
Why does the arrow function use curly braces instead of parentheses like the previous arrow function
const FormCard = (props) => (
const FormCard = (props) => (
<div>
{
DATA.map((props) => {
return <div style={{...largebox, ...flex}} key={props.id}>
<div style={{...Photo,backgroundImage: `url(${props.photo})`}}></div>
<div>
<Author author={props.author}/>
<Something bio={props.bio}/>
<AdBox adpic={props.adpic} />
<IconBox />
</div>
</div>
})
}
</div>
)
These are two different ways of returning from arrow functions.
The implicit return:
If the body starts with an expression and not with a { is seen as a value to be returned.
[0,1,2,3,4,5,6].map(v => ({value:v})); // gives an array of objects with value set to v.
[0,1,2,3,4,5,6].map(v => v*v)// gives an array of squares of the initial array.
the explicit return:
If the body starts with a {, it seen as the body of the function, and a return statement is expected to return.
[0,1,2,3,4,5,6].map(v => { return {value:v}}); // gives an array of objects with value set to v.
[0,1,2,3,4,5,6].map(v => { return v*v})// gives an array of squares of the initial array.
Generally,
array.map((arg) => { return actionWith(arg) })
array.map((arg) => actionWith(arg))
Are equal, thus developers shrink their functions if they have returns only
This question already has answers here:
Arrow function without curly braces
(9 answers)
Closed 4 years ago.
We create presentational component or stateless component like this
const MyComponent = () => {
return(<div>my component</div>)
}
but I'd seen this
const MyComponent = () =>
<div>
<h1>head</h1>
my component
</div>
so now I'm confused when the braces is needed when using es6's arrow function.
This confused me on when rendering a list using map
shorter version
<div>
{map(o =>
<div>{o.name}</div>
)}
</div>
longer version
<div>
{map(o => {
return(<div>{o.name}</div>)
})}
</div>
Both are correct, but why write longer?
{map(o => // without curly brackets
<div>{o.name}</div> // this will be returned implicitly
)}
{map(o => { // with curly brackets
return <div>{o.name}</div> // you need to return explicitly
}
)}
If you do curly brackets ,
You have to explicilty return the data ,
When to use which one?
When you have mutliple line of execution you need to do curly brackets and return from it
But if you have single line of execution, that you need to return , then there is no need of curly brackets and return , it will return implicitly.
Same as If condition
if(true)
// do this for single line
else
// do this for single line
if() {
// do this for multiple line
} else {
// do this for multiple line
}
Arrow functions work both way to provide you with a bit of versatility. Say you need to perform some logic inside your function before you return, in this case you would need to add curly braces, i.e say you need to extract the name of a list of users, but you want to append their title.
let users = [new User(), ... ];
//...
let withTitle = users.map(p => {
const title = getTitle(p); // automagically returns Mr, Mrs, etc
return `${title} ${p.fullName}`
});
// withTitle => ['Mr Ricky Bobby', 'Mr Ron Burgundy']
Now, you can declare a function that does the work for you, and use the shorthand version of the arrow function. like so.
const extractWithTitle: (user) => {
const title = getTitle(p); // automagically returns Mr, Mrs, etc
return `${title} ${p.fullName}`
}
let withTitle = users.map(p => extractWithTitle(p));
// withTitle => ['Mr Ricky Bobby', 'Mr Ron Burgundy']
Now, an even shorter way to approach this would be to pass a reference to the function.
users.map(extractWithTitle);
Both are correct, but why write longer?
You basically need to use the longer version if you need to add more sentences in you arrow function other than the jsx component.
E.g.
<div>
{map(o => {
const name = "My name is: " + o.name;
return(<div>{name}</div>)
})}
</div>
Otherwise, you may use the short version.