This question already has answers here:
How do I get the right "this" in an Array.map?
(5 answers)
Closed 3 years ago.
For example I have a react component with two binding methods:
import React from 'react';
class Comments extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemoveComment = this.handleRemoveComment.bind(this);
}
handleSubmit(e) {
.....
}
handleRemoveComment(e) {
//this.props.removeComment(null, this.props.params, i);
}
renderComment(comment, i) {
return(
<div className="comment" key={i}>
.....
<button
onClick={this.handleRemoveComment}
className="remove-comment">
×
</button>
</div>
)
}
render() {
return(
<div className="comments">
{this.props.postComments.map(this.renderComment)}
.....
</div>
)
}
}
export default Comments;
In above code, I have two binding method: one is handleSubmit and one is handleRemoveComment. handleSubmit function worked but handleRemoveComment doesn't. When running, It returns error:
TypeError: Cannot read property 'handleRemoveComment' of undefined
Issue is with this line:
{this.props.postComments.map( this.renderComment )}
Because you forgot to bind renderComment, map callback method, so this inside renderComment method will not refer to the class context.
Use any one of these solutions, it will work.
1- Use this line in constructor:
this.renderComment = this.renderComment.bind(this) ;
2- Pass this with with map like:
{this.props.postComments.map(this.renderComment, this)}
3- Use Arrow function with renderComment method, like this:
renderComment = (comment, i) => {
.....
or use the map inside the renderComment function (i used to prefer this way), like this:
renderComment() {
return this.props.postComments.map((comment, i) => {
return(
<div className="comment" key={i}>
<p>
<strong>{comment.user}</strong>
{comment.text}
<button
onClick={this.handleRemoveComment}
className="remove-comment">
×
</button>
</p>
</div>
)
})
}
And call this method from render, in this case binding of renderComment is not required:
{this.renderComment()}
Related
I'm trying to create a simple dashboard. I'm just exploring some new ideas I have in react and it's been so long I'm running into a strange problem I can't seem to understand.
I have a very simple class:
export default class Dashboard extends React.Component {
constructor(){
super();
}
HandleClick = (e) => {
if (e.name === "createEvent") {
console.log('event clicked');
}
console.log(e.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={this.HandleClick}/>
</div>
)
}
}
and then I have a simple function outside of the class that creates a button list:
function ButtonList(props) {
return (
<button name="createEvent" onClick={props.HandleClick}>Create Event</button>
)
}
the idea behind this was instead of having so much stuff inside one superclass I wanted to separate simple functionality, like a button or command list if you will, that opon clicking would eventually change the state of the navbar.
I'm not sure how I would return that values of the button, or aside from that pass a parameter into the button from a child prop.
For example instead of doing HandleClick = (e) => and actually look for a parameter, how would I pass that in the child function where it gets used (if there were many more buttons)?
This is what you should be doing instead:
On your parent component, you can use arrow functions to pass the parameters within handleClick. This will allow you to listen to the events on your child ButtonList component with the parameters passed onto the method.
In addition, if you want to access to name attribute of your button, you should be calling event.target.name, as name is part of the target property of the Event interface.
export default class Dashboard extends React.Component {
constructor(){
super();
}
handleClick = (e) => {
if (e.target.name === "createEvent") {
console.log('event clicked');
}
console.log(e.target.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={(e) => this.handleClick(e)} />
</div>
)
}
}
And on your ButtonList functional component, you should pass the onClick event to the onClick props which was defined as part of the ButtonList component.
function ButtonList(props) {
const onClick = (e) => {
props.onClick(e);
};
return (
<button name="createEvent" onClick={(e) => onClick(e)}>Create Event</button>
)
}
I have created a demo over here.
Im trying to use a dynamic style property. The approach below throws me an "The style prop expects a mapping from style properties to values, not a string" error.
class someClass extends React.Component {
someFunction = () => {
return {marginLeft : 20 };
}
render() {
return( <div style={this.someFunction}/>
);
}
}
Howerver this one works:
class someClass extends React.Component {
render() {
return( <div style={{marginLeft : 20}}/>
);
}
}
Why is that so and how can i return style objects from functions?
Thanks for any answers in advance!
You didn't call the function inside the style props JSX. Call it like this.someFunction(), then it will return the object of style you kept inside the someFunction.
return <div style={this.someFunction()} />
This question already has answers here:
React: "this" is undefined inside a component function
(11 answers)
Closed 4 years ago.
Hi Trying to learn React right now, still making just baby steps.
I wrote code bellow in codepen(see link on the bottom), in my code I put a few log to console statements I can't figure out why my function handleSubmit which is inside upmost component('TodoApp') cannot access state?
I figured it cannot access it because I can print to console text just above 'let current_todos = this.state.todos' but I never see in console text just bellow it.
If this is incorrect how am I supposed to access state then?
NOTE: I realize that a lot of code in that function is redundant but I declare these variables and log statements for debugging purposes
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
todos : [ ]
}
}
render() {
return (
<div className='todo-comp todo-app'>
<h2>ToDo App</h2>
<form onSubmit={this.handleSubmit}>
<input type="text">
</input>
</form>
<TodoList todos={this.state.todos}/>
</div>
)
}
handleSubmit(event) {
let new_todo = event.target.children[0].value
console.log("Submited: ".concat(new_todo))
let current_todos = this.state.todos
console.log("Succesfully accessed state")
this.setState({"todos" : this.state.todos.push(new_todo)})
}
}
class TodoList extends React.Component {
constructor(props) {
super(props)
}
render () {
return (
<ul className="todo-comp todo-list">
{this.props.todos.map(
function(item,key) {
return(
<li key={key} className="todo-comp todo-item">{item}</li>
)
})}
</ul>
)
}
}
ReactDOM.render(
<TodoApp />,
document.getElementById('app'),
console.log("App has been rendered"))
My CodePen Link
The first mistake is that your handleSubmit will be recreated on every render.
This code will allow you to see the input value and submit etc. Hope this helps, if you have anymore questions just comment underneath.
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
this.onChange= this.onChange.bind(this)
this.state = {
todos : [ ]
}
}
onChange(event) {
this.setState({ text: e.target.value })
}
handleSubmit(event) {
const { text } = this.state;
// Your submit value;
console.log(text)
}
render() {
return (
<div className='todo-comp todo-app'>
<h2>ToDo App</h2>
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.onChange}>
</input>
</form>
<TodoList todos={this.state.todos}/>
</div>
)
}
}
When calling this.handleSubmit, you should add .bind(this), since the context is different on invocation time. Another option would be to add the following line in the constructor:
this.handleSubmit = this.handleSubmit.bind(this)
I am sort of new to React and just started reading the Road to learn React free ebook. Anyway there is a part in the book where it says that in order to access this inside a class method, we need to bind class method to this. An example that is provided there clearly shows that:
class ExplainBindingsComponent extends Component {
onClickMe() {
console.log(this);
}
render() {
return (
<button
onClick={this.onClickMe}
type="button"
>
Click Me
</button>
);
}
}
when the button is clicked, this is undefined, and if I add a constructor with this:
constructor() {
super();
this.onClickMe = this.onClickMe.bind(this);
}
I can access this inside a method. However, I am now confused because I'm looking at an example where there's no binding and this is accessible:
class App extends Component {
constructor(props) {
super(props);
this.state = {
list,
};
}
onDismiss(id) {
console.log(this);
const updatedList = this.state.list.filter(item => item.objectID !== id);
this.setState({list: updatedList});
}
render() {
return (
<div className="App">
{this.state.list.map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button
onClick={() => this.onDismiss(item.objectID)}
type="button"
>
Dismiss
</button>
</span>
</div>
)}
</div>
);
}
}
Inside onDismiss I can print this without any problems, although I didn't bind it ? How is this different from the first example ?
Cause of these four characters:
() =>
Thats an arrow function. Unlike regular functions, they don't have their own context (aka this) but rather take the one of their parent (render() in this case) and that has the right context.
When you declare as a function using () => it automatically binds itself to this.
Take a look here:
https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4
This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 7 years ago.
Im currently a little stuck with the following.
function Tag(props){
let {text, onRemove, key} = props;
return (
<span onClick={onRemove(key)} key={key}>{text}</span>
)
}
function TagInput (props) {
let {onChange, value, onKeyDown} = props;
return (
<input type="text" onChange={onChange} value={value} onKeyDown={onKeyDown} />
)
}
class TagsInput extends Component {
render() {
let { Tag, TagInput, state } = this.props;
console.log(state.tags);
return (
<div ref="div" onClick={(e) => this.handleClick(e)}>
{state.tags.map((tag) =>
<Tag
text={tag.text}
key={tag.id}
onRemove={this.handleRemove}
/>
)}
<TagInput
onChange={(e) => this.handleChange(e)}
onKeyDown={(e) => this.handleKeyDown(e)}
/>
</div>
)
}
handleClick(e) {
console.log(e.target.value);
}
handleChange(e){
//console.log(e.target.value);
}
handleKeyDown(e){
//console.log('keycode', e.keyCode);
const { dispatch } = this.props;
if (e.keyCode === 32) {
dispatch(addTag(e.target.value));
}
if (e.keyCode === 8 && e.target.value.length === 0) {
dispatch(removeTag());
}
}
handleRemove(id){
const { dispatch } = this.props;
dispatch(removeTag(id));
}
}
TagsInput.propTypes = {
TagInput: React.PropTypes.func,
Tag: React.PropTypes.func,
removeKeyCodes: React.PropTypes.array,
addKeyCodes: React.PropTypes.array
};
TagsInput.defaultProps = {
TagInput: TagInput,
Tag: Tag,
removeKeyCodes: [8],
addKeyCodes: [9, 13]
};
I get the following error in console Uncaught TypeError: Cannot read property 'props' of undefined from method handleRemove line const { dispatch } = this.props. It seems like a scoping issue but I cannot seem to understand why this (no pun intended lol) is occurring.
ES6 classes do not automatically bind this to functions with the exception of the ones provided by extending Component such as componentDidMount etc..
from the docs
The ES6 way - bind this to your methods in your constructor, OR where you call them:
class TagsInput extends Component {
constructor (props) {
super(props)
this.handleRremove = this.handleRemove.bind(this)
}
OR
render() {
return (
<Tag
onRemove={this.handleRemove.bind(this)}
/>
}
The ES7 way #1: bind syntax
this.handleRemove = ::this.handleRemove
The ES7 way #2: class arrow functions (I think this is the best way):
handleRemove = (id) => {
const { dispatch } = this.props;
dispatch(removeTag(id));
}
Then call it like:
onRemove={ () => this.handleRemove(tag.id) }
UPDATE: Read #Road's answer as well. When binding the method at point of use you need to pass the argument along.
Have you tried binding this? Try this.handleRemove.bind(this, tag.id). The tag.id there is the argument you're passing since handleRemove(id) needs an id as an arg.