I decided to learn React and started with the official tutorial. All is good until I get to this state of my code:
var CommentBox = React.createClass({
render: () => {
return (
<div className="commentBox">
<h1> Comments </h1>
<CommentList />
<CommentForm />
</div>
);
}
});
var CommentForm = React.createClass({
render: () => {
return (
<div className="commentForm">
Hello, world! I am a comment form;
</div>
);
}
});
var Comment = React.createClass({
rawMarkup: () => {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return {__html: rawMarkup};
},
render: () => {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2> // <--- [[[[[[ ERROR IS HERE ]]]]]]
<span dangerouslySetInnerHtml={this.rawMarkup} />
</div>
);
}
});
var CommentList = React.createClass({
render: () => {
return (
<div className="commentList">
<Comment author="Pete Hunt">This is one comment</Comment>
<Comment author="Jordan Walke">This is *another* comment yo</Comment>
</div>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
When I try to run it, I get the following error in devtools:
TypeError: Cannot read property 'props' of undefined
...and the debugger pauses at the marked line (see code). When I mouseover this in {this.props.author}, I get a preview of the object which has the props property and everything...
Use function declaration ( render() {} or render: function {}) instead of arrow function render: () => {}
var Comment = React.createClass({
rawMarkup() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return {__html: rawMarkup};
},
render() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHtml={this.rawMarkup} />
</div>
);
}
});
Example
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target).
Arrow functions are always anonymous.
I had the same error message:
Cannot read property 'props' of undefined
...but from a different cause: when this is called from within a function, javascript can not reach the variable because this is in an outer scope. (Note: I was in ES5)
In this case, simply store this in another variable, prior to the function (in the scope of your component): var that = this;
Then you will be able to call that.props from within the function.
Hope this helps for other people who had that error message.
Detailed example below:
render: function() {
var steps = [];
var that = this; // store the reference for later use
var count = 0;
this.props.steps.forEach(function(step) {
steps.push(<Step myFunction={function(){that.props.anotherFunction(count)}}/>); // here you are
count += 1;
});
return (
<div>{steps}</div>
)
}
A little late post/answer.
Try to bind your function inside the constructor
example:
this.yourfunction = this.yourfunction.bind(this);
I am on ES6 and the arrow function did the trick: rawMarkup = () => {}
Related
This works perfectly fine. Now, I would like to incorporate this into a function and just call the function within my expression. It doesn't work.
Working code:
render: function() {
return (
<div>
{this.props.list.map(function(listValue){
return <p>{listValue}</p>;
})}
</div>
)
}
});
ReactDOM.render(<List list={[1,2,3,4,5]} />, document.getElementById('app'));
code with the function (doesn't work):
var List = React.createClass({
addText: function()
{
this.props.list.map(function(listValue){
return <p>{listValue}</p>;
});
},
render: function() {
return (
<div>
{this.addText()}
</div>
)
}
});
ReactDOM.render(<List list={[1,2,3,4,5]} />, document.getElementById('app'));
You've extracted the call to map into another function and maybe assumed the return call inside it was sufficient to return the result of the whole call (it just returns the mapped value for that iteration of map).
You just need to return the result of your map in your addText function:
var List = React.createClass({
addText: function()
{
return ( // <------ADD THIS
this.props.list.map(function(listValue){
return <p>{listValue}</p>;
})
); //<<-----DON'T FORGET TO CLOSE OFF THE NEW BRACKET
},
render: function() {
return (
<div>
{this.addText()}
</div>
)
}
});
Tnx #Thomas altmann, I forgot to make another return prior to my first return
var List = React.createClass({
addText: function()
{
return (this.props.list.map(function(listValue){
return <p>{listValue}</p>;
})
);
},
render: function() {
return (
<div>
{this.addText()}
</div>
)
}
});
ReactDOM.render(<List list={[1,2,3,4,5]} />, document.getElementById('app'));
I have 2 files:
grid-body.jsx (GridBody) and grid-row.jsx (GridRow)
In GridBody, I declared a function showAlert which I pass to every GridRow:
var GridBody = React.createClass({
showAlert: function(msg) {
alert(msg);
},
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
});
return (
<div>
{rows}
</div>
);
}
});
And in GridRow:
var GridRow = React.createClass({
toggle: function() {
this.props.showAlert('HEY'); // -----> ERROR - not a function
},
render: function() {
<div>
<a href="#" onClick={this.toggle} />
</div>
}
});
I'm trying to call the showAlert from parent and based on the examples I've seen, this is how to do it but I can't make it work.
you're using the wrong value for this inside of GridView.render. Either pass it explicitly to Array.map() (see the docs for how to do that) or assign this to some new variable at the very top of render() and reference that instead.
Here is a really, really great SO comment as to why this happens, as well as some other alternative workarounds if neither of the above work for you.
The context of the function passed to map in render method of GridBody is window and not the component. You can bind the interatee to get the behavior you want:
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
}.bind(this));
return (
<div>
{rows}
</div>
);
}
I have a react component that passes a function to a child component, and I bind it to onClick. But when I click I get an error:
this.props.beBad is not a function, this.props.beBad is not defined
it looks something like this:
var Dad = React.createClass({
beBad: function(someInput) {
alert('being bad ' + someInput);
},
render: function() {
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/>
);
});
return (
<div>
{Children}
</div>
);
});
var Child = React.createClass({
beBad: function() {
this.props.beBad('some input');
},
render: function() {
return(
<div onClick={this.beBad}>
be bad
</div>
);
}
});
It doesn't look like this is what you expect. When using React.createClass, React will autobind the correct this IFF the call is at the top level of the function, so in this case it will autobind correctly:
render: function() {
return (
<div onClick={this.beBad}> // autobound
be bad
</div>
);
}
But not here:
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/> // `this` is not what you expect
);
});
return (
<div>
{Children}
</div>
);
The reason is because map is creating a new scope, and this is no longer what you'd expect, and React can't autobind it for you. You need to pass this around explicitly:
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad.bind(this)} key={index}/> // bind `this`
);
}, this); // pass `this`
Note: Seeing the other response, it is possible that React autobinds it for you inside the map, but in general I wouldn't rely on autobinding, explicit wins over implicit. Plus if you ever write React with ES6 classes it doesn't do autobinding.
You've got a problem with your this because you're on the "map scope".
Try to pass this to your map :
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/>
);
}, this);
For your information see : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Using backbone-react-component I am trying to build a task list of sorts. If you look at the snipplet provided you will see to core variables tasksList and tasksListChild of which my code thus far seems to render as far as _createTaskChild But Rendering beyond that I have nothing, literally, not even an error in the console window. I put console.log to better elaborate here where things are kind of failing?.. console.log(model.get('task_id')); will put out to the console no problem. console.log('hi') however, does not. I'm hoping by coming here an extra set of eyes can tell me where I messed up.
var tasksListChild = React.createClass({
mixins: [BackboneReactMixin],
render: function () {
console.log('hi')
return (
<span onClick={ this._handleTaskClick }>nothing</span>
);
},
_handleTaskClick: function (event) {
console.log(this.props)
}
});
var tasksList = React.createClass({
mixins: [BackboneReactMixin],
render: function () {
return (
<div className="list-group js__page-list-group-tasks">
{
this.getCollection().map(this._createTaskChild)
}
</div>
);
},
_createTaskChild: function (model) {
console.log(model.get('task_id'));
return (
<a className="list-group-item">
<tasksListChild key={model.get('task_id')} model={model} />
</a>
);
}
});
Will this work.
_createTaskChild: function (model) {
var taskID = model.get('task_id');
console.log(taskID);
return (
<a className="list-group-item">
<tasksListChild key={taskID} model={model} />
</a>
);
}
I am not even sure where the issue is, the only thing I can think of is due to using .map() this is getting lost in translation. What I have below works as I could hope overall until I start trying to add in the onClick which gives me the error in the subject of this post. I need a pair of eyes to take a look and tell me what i did where I went wrong here.
var myApp = React.createClass({
mixins: [Backbone.React.Component.mixin],
_myPrivateFunction: function (objId) {
console.log(objId);
},
render: function () {
return (<ul className="list-group">
{this.state.collection.map(function (system) {
return (<li className="list-group-item" onClick={this._myPrivateFunction.bind(this, system.objId)}>{system.objName}</li>);
})}
</ul>);
}
});
Try
var myApp = React.createClass({
mixins: [Backbone.React.Component.mixin],
_myPrivateFunction: function (objId) {
console.log(objId);
},
render: function () {
var that = this;
return (<ul className="list-group">
{this.state.collection.map(function (system) {
return (<li className="list-group-item" onClick={that._myPrivateFunction.bind(that, system.objId)}>{system.objName}</li>);
})}
</ul>);
}
});
The scope of this is probably changing inside that function/event.