Reputation: 1903
I want to add a handler on <a>
in map
:
My simple functions:
getFacilities = (e) => {
console.log(e)
}
In render:
const items = this.state.data;
let result = items.map(function(value, i){
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
});
TypeError: Cannot read property 'getFacilities' of undefined
There are many topics with this error, Almost all of them use this as solution:
I add this in constructor
this.getFacilities = this.getFacilities.bind(this)
But it not working in my case and still return same error. it's simple, confused can not figure out what I missed I done wrong.
Upvotes: 0
Views: 95
Reputation: 63589
The key thing to note is the difference between normal function expressions and the new ES6 arrow functions. In short: unlike function expressions arrow functions do not maintain their own this
(or arguments
) - they borrow it from the outer lexical environment.
"An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors."
So when you refer to this
in that map
callback this
doesn't refer to the class.
So 1) change the function expression to an arrow function:
let result = items.map((value, i) => {
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
});
2) Since you're using an arrow function for your class method you can remove as this would only be required if you were using a standard class method definition rather than a class field.
this.getFacilities = this.getFacilities.bind(this);
However there's some evidence to suggest that class fields (using arrow functions) such as these are worse than using standard class method format, so you may be better off keeping the binding in the constructor and using the standard format for functions that you're passing around:
getFacilities(e) {
console.log(e)
}
It's something to bear in mind.
PS, from that link:
"It is unnecessary to do that to every function. This is just as bad as autobinding (on a class). You only need to bind functions that you pass around. e.g.
onClick={this.doSomething}
. Orfetch.then(this.handleDone)
— Dan Abramov"
Upvotes: 1
Reputation: 3117
Please dont use Arrow function
class Name extends Component {
constructor(props) {
super(props);
this.getFacilities = this.getFacilities.bind(this);
}
getFacilities(e) {
// remove Arrow function
console.log(e);
}
render() {
return (
<div>
{this.state.data.map(function(value, i) {
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
})}
</div>
);
}
}
Upvotes: 0
Reputation: 3270
You need to change map
predicate to arrow function (to bind the this
context to your component not to the map
function)
let result = items.map((value, i) => {
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
});
After that, you need to change the getFacilities
declaration
Solution 1 - Remove this line from constructor
this.getFacilities = this.getFacilities.bind(this)
Solution 2 - But if you want, you can change getFacilities
to hoisting function
getFacilities(e) {
console.log(e)
}
And keep this line in constructor
this.getFacilities = this.getFacilities.bind(this)
Upvotes: -1
Reputation: 10873
To ensure proper binding of this
, use arrow function in the map
callback:
let result = items.map((value, i) => {
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
});
Upvotes: 1
Reputation: 1313
Because this
inside your map function is not defined. Use an arrow function to bind the map function to the parent scope:
const items = this.state.data;
let result = items.map((value, i) => {
return (
<List.Item key={i}>
<a onClick={this.getFacilities}>{value.user.name}</a>
</List.Item>
);
});
Upvotes: 1