Reputation: 1558
So, I wish to call a function using an onClick event and pass it some parameters from the event, but am receiving the above mentioned error message.
What am I overlooking here?
My code is as follows:
class Comments extends React.Component {
constructor(props) {
super(props);
this.removeCommentMutation = this.removeCommentMutation.bind(this);
}
removeCommentMutation (postID, commentID) {
....
}
handleSubmitError (err) {
console.error(err.message);
}
renderComment (comments) {
return (
<div className="comment" key={comments.id}>
<p>
<strong>{comments.user}</strong>
{comments.text}
<button className="remove-comment" onClick={() => this.removeCommentMutation(this.props.postId, comments.id)}>×</button>
</p>
</div>
);
}
handleSubmit (e) {
e.preventDefault();
this.addCommentMutation(this.props.postId, this.refs.author.value, this.refs.comment.value);
this.refs.commentForm.reset();
this.refs.author.focus();
}
render () {
const comments = this.props.post.comments || [];
const currentPosts = comments.map(this.renderComment);
return (
<div className="comments">
{currentPosts}
<form onSubmit={this.handleSubmit} ref="commentForm" className="comment-form">
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden/>
</form>
</div>
);
}
};
}
The full error is:
Comments.js:189 Uncaught TypeError: Cannot read property 'removeCommentMutation' of undefined
at onClick (http://localhost:7770/static/bundle.js:36072:30)
at HTMLUnknownElement.wrapped (http://localhost:7770/static/bundle.js:63526:29)
at Object.ReactErrorUtils.invokeGuardedCallback (http://localhost:7770/static/bundle.js:43148:16)
at executeDispatch (http://localhost:7770/static/bundle.js:70629:21)
at Object.executeDispatchesInOrder (http://localhost:7770/static/bundle.js:70652:5)
at executeDispatchesAndRelease (http://localhost:7770/static/bundle.js:7436:22)
at executeDispatchesAndReleaseTopLevel (http://localhost:7770/static/bundle.js:7447:10)
at Array.forEach (native)
at forEachAccumulated (http://localhost:7770/static/bundle.js:44180:9)
at Object.processEventQueue (http://localhost:7770/static/bundle.js:7652:7)
at runEventQueueInBatch (http://localhost:7770/static/bundle.js:74532:18)
at Object.handleTopLevel [as _handleTopLevel] (http://localhost:7770/static/bundle.js:74548:5)
at handleTopLevelWithoutPath (http://localhost:7770/static/bundle.js:74651:24)
at handleTopLevelImpl (http://localhost:7770/static/bundle.js:74631:3)
at ReactDefaultBatchingStrategyTransaction.perform (http://localhost:7770/static/bundle.js:12582:20)
at Object.batchedUpdates (http://localhost:7770/static/bundle.js:42559:19)
at Object.batchedUpdates (http://localhost:7770/static/bundle.js:2907:20)
at dispatchEvent (http://localhost:7770/static/bundle.js:74762:20)
at HTMLDocument.wrapped (http://localhost:7770/static/bundle.js:63526:29)
Upvotes: 0
Views: 78
Reputation: 8794
You need to bind
all of your functions. In React, the context of this
is undefined
. That is until you use bind()
to change the context to be your Component.
You can do this one of two ways.
Using .bind()
class Comments extends React.Component {
constructor(props) {
super(props);
this.removeCommentMutation = this.removeCommentMutation.bind(this);
this.handleSubmitError = this.handleSubmitError.bind(this);
this.renderComment = this.renderComment.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
removeCommentMutation (postID, commentID) {
....
}
handleSubmitError (err) {
console.error(err.message);
}
renderComment (comments) {
return (
<div className="comment" key={comments.id}>
<p>
<strong>{comments.user}</strong>
{comments.text}
<button className="remove-comment" onClick={() => this.removeCommentMutation(this.props.postId, comments.id)}>×</button>
</p>
</div>
);
}
handleSubmit (e) {
e.preventDefault();
this.addCommentMutation(this.props.postId, this.refs.author.value, this.refs.comment.value);
this.refs.commentForm.reset();
this.refs.author.focus();
}
render () {
const comments = this.props.post.comments || [];
const currentPosts = comments.map(this.renderComment);
return (
<div className="comments">
{currentPosts}
<form onSubmit={this.handleSubmit} ref="commentForm" className="comment-form">
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden/>
</form>
</div>
);
}
};
}
Using Arrow functions - ES6
Typically the context of this
in JavaScript is determined by how a function is called. With arrow functions, the context is lexical, meaning that this
is determined by the outer scope (which in this case is your Comments component).
You can use arrow functions like so, which would mean you don't have to constantly bind()
every single method.
class Comments extends React.Component {
constructor(props) {
super(props);
}
removeCommentMutation = (postID, commentID) => {
....
}
handleSubmitError = (err) => {
console.error(err.message);
}
renderComment = (comments) => {
return (
<div className="comment" key={comments.id}>
<p>
<strong>{comments.user}</strong>
{comments.text}
<button className="remove-comment" onClick={() => this.removeCommentMutation(this.props.postId, comments.id)}>×</button>
</p>
</div>
);
}
handleSubmit = (e) => {
e.preventDefault();
this.addCommentMutation(this.props.postId, this.refs.author.value, this.refs.comment.value);
this.refs.commentForm.reset();
this.refs.author.focus();
}
render () {
const comments = this.props.post.comments || [];
const currentPosts = comments.map(this.renderComment);
return (
<div className="comments">
{currentPosts}
<form onSubmit={this.handleSubmit} ref="commentForm" className="comment-form">
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden/>
</form>
</div>
);
}
};
}
Notice the foo = () => {}
syntax. You don't have to do this for Component lifecycle methods e.g. componentWillMount
, componentDidMount
and you don't have to do this for render
.
Upvotes: 2