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>
);
}
Related
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 = () => {}
var Component = React.createClass({
onClickButton : function(){
//i want layer in this function
},
render: function () {
return (
<div className="Component">
<button onClick={this.onClickButton}>Click Me</button>
</div>
);
}
});
function renderNow(data,layer){
ReactDOM.render(
<Component data={data} />,
layer
);
}
called using renderNow({name: 'John' },someLayer);
I want layer inside the onClickButton function which was passed through renderNow function.
I tried passing layer to render function and setState the layer but it gave me StackOverflow Error
I am not sure what exactly the layer is and what you would like to achieve, but I suppose you should be able to pass layer as a props to your Component.
function renderNow(data,layer){
ReactDOM.render(
<Component data={data} layer={layer} />,
layer
);
}
and then
...
onClickButton : function(){
this.props.layer //i want layer in this function
},
...
Marek's answer is the way I would do it...but there's another way using ReactDOM.findDOMNode()
var Component = React.createClass({
onClickButton : function() {
//i want layer in this function
const layer = ReactDOM.findDOMNode(this); // <-- Here you go
},
render: function () {
return (
<div className="Component">
<button onClick={this.onClickButton}>Click Me</button>
</div>
);
}
});
Ref: https://facebook.github.io/react/docs/react-dom.html
Notice:
The docs generally recommend against breaking through the Component abstraction:
Note:
findDOMNode is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.
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 have following React.js application structure:
<App />
<BreadcrumbList>
<BreadcrumbItem />
<BreadcrumbList/>
<App />
The problem is, when I click on <BreadcrumbItem /> , I want to change a state in <App />
I used callback to pass props to <BreadcrumbList/> but that`s how far I got.
Is there any pattaren how to easily pass props up to compenent tree ?
How can I pass prop to <App />, without doing any callback chaining ?
If you are doing something simple then its often just better to pass the change in state up through the component hierarchy rather than create a store specifically for that purpose (whatever it may be). I would do the following:
BreadcrumbItem
var React = require('react/addons');
var BreadcrumbItem = React.createClass({
embiggenMenu: function() {
this.props.embiggenToggle();
},
render: function() {
return (
<div id="embiggen-sidemenu" onClick={this.embiggenMenu} />
);
}
});
module.exports = BreadcrumbItem ;
THEN pass it up to the parent through the BreadcrumbList component.....
<BreadcrumbItem embiggenToggle={this.props.embiggenToggle}>
... and UP to App, then use it to set the state....
var React = require('react/addons');
var App = React.createClass({
embiggenMenu: function() {
this.setState({
menuBig: !this.state.menuBig
});
},
render: function() {
return (
<div>
<BreadcrumbList embiggenToggle={this.embiggenMenu} />
</div>
)
}
});
module.exports = BreadcrumbItem;
This example toggles a simple boolean however you can pass up anything you like. I hope this helps.
I have not tested this but it was (quickly) ripped from a live working example.
EDIT:
As it was requested i'll expand upon the vague: "you can pass up anything".
If you were making a navigation menu based on an array and needed to pass up the selected item to a parent then you would do the following
var React = require('react/addons');
var ChildMenu = React.createClass({
getDefaultProps: function () {
return {
widgets : [
["MenuItem1"],
["MenuItem2"],
["MenuItem3"],
["MenuItem4"],
["MenuItem5"],
["MenuItem6"],
["MenuItem7"]
]
}
},
handleClick: function(i) {
console.log('You clicked: ' + this.props.widgets[i]);
this.props.onClick(this.props.widgets[i]);
},
render: function() {
return (
<nav>
<ul>
{this.props.widgets.map(function(item, i) {
var Label = item[0];
return (
<li
onClick={this.handleClick.bind(this, i)}
key={i}>
{Label}
</li>
);
}, this)}
</ul>
</nav>
);
}
});
module.exports = ChildMenu;
You would then do the following in the parent:
var React = require('react/addons');
var ChildMenuBar = require('./app/top-bar.jsx');
var ParentApp = React.createClass({
widgetSelectedClick: function(selection) {
//LOGGING
//console.log('THE APP LOGS: ' + selection);
//VARIABLE SETTING
var widgetName = selection[0];
//YOU CAN THEN USE THIS "selection"
//THIS SETS THE APP STATE
this.setState({
currentWidget: widgetName
});
},
render: function() {
return (
<ChildMenu onClick={this.widgetSelectedClick} />
);
}
});
module.exports = ParentApp;
I hope this helps. Thanks for the upvote.
If you use Flux pattern, you can have a AppStore which listen a BREADCRUMB_CLICK event. So when you click on a BreadCrumbItem, you can execute an action which dispatch BREADCRUMB_CLICK event. When AppStore handle the event, he inform App component which update your state.
For more informations:
Flux architecture