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
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 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>
);
}
Is there way to pass props to a generic child (not a component that you know ahead)?
Something that would make Wrapper be able to pass foo to children.
var Wrapper = React.createClass({
render: function () {
return <div>
{this.props.children foo={2}}
</div>
}
});
var App = React.createClass({
render: function () {
return (
<Wrapper>
{this.props.foo}
</Wrapper>
)
}
});
jsfiddle
Imagine Javascript code
this.props.children foo=2
this is what your expression is transpiled into from JSX into plain JS. The fact is, you can't pass props to children directly because children isn't a React component. To make it work, you need to map through children and pass your props per every item of iterable.
The problem that comes next is that you can't simply do
this.props.children.map(child => (
<Child foo={2} />
))
because, first, you'll receive TypeError because map is undefined, and second, you'd lose all initial props of every child.
You'll need to use React.Children.map static function as well as React.cloneElement to make it work:
React.Children.map(children, child => React.cloneElement(child, {
foo: 2
}))
This way, every child element preserves its own props passed from parent element and, in addition to them, receive new props you define. Be careful with it because you may unintentionally redefine values of some props, too.
Your example code will then look like
var Wrapper = React.createClass({
render: function () {
const {
foo
} = this.props;
return (
<div>
{React.Children.map(this.props.children, child => React.cloneElement(child, {
foo
}))}
</div>
);
}
});
var App = React.createClass({
render: function () {
return (
<Wrapper foo={2}>
<div>I should be a component</div>
<div>I should be a component, too</div>
<div>We all should be components</div>
</Wrapper>
);
}
});
as shown in the example below, I'd like MyComponent to dynamically attach an "onClick" event to its children. The onClick event should fire alertView that should be able to call the clicked element method "getValue".
JSFiddle: http://jsfiddle.net/2g638bp8/
How to do this? Thanks
var MyComponent = React.createClass({
alertValue: function () {
// RETRIEVE THE CHILD HERE
alert(child.getValue());
},
render: function () {
var children = React.Children.map(this.props.children, function (c, index) {
return React.addons.cloneWithProps(c, {
ref: 'child-' + index
});
});
return (
<div>
{children}
</div>
);
}
});
var MySubComponent = React.createClass({
getValue: function () {
return this.props.val;
},
render: function () {
return (
<div>{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent>
<MySubComponent val="1" />
<MySubComponent val="2" />
<MySubComponent val="3" />
</MyComponent>
</div>,
document.getElementById('container')
);
You can't call methods on child components in React. You can only set properties. (The child is actually a ReactElement which contains information about the class and associated properties. It is not an instance of the component you created).
So, you could think about this a slightly different way and move the onClick to the MySubComponent:
var MyComponent = React.createClass({
onHandleGiveValue: function (value) {
alert(value);
},
render: function () {
const children = React.Children.map(this.props.children, child => React.cloneElement(child, { onGiveValue: this.onHandleGiveValue.bind(this) }));
return (
<div>
{children}
</div>
);
}
});
var MySubComponent = React.createClass({
handleClick: function() {
this.props.onGiveValue(this.props.val);
},
getValue: function () {
return this.props.val;
},
render: function () {
return (
<div onClick={ this.handleClick } >{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent>
<MySubComponent val="1" />
<MySubComponent val="2" />
<MySubComponent val="3" />
</MyComponent>
</div>,
document.getElementById('container')
);
By doing that, your code can pass the current value as an event to the parent component. I've created a new event from the MySubComponent class named onGiveValue. That for now just passes the value from this.props.val. But, it could of course be anything.
Pass the parent callback to the subComponent, one dont need a reference for the child component.
React prefers composition design pattern, So your parent component should contains those three subComponent.
JS Fiddle: http://jsfiddle.net/68vt3umg/
var MyComponent = React.createClass({
handleChildClick: function (e, childValue) {
alert(childValue);
},
render: function () {
return (
<div>
<MySubComponent val="1" onSubClicked={this.handleChildClick}/>
<MySubComponent val="2" onSubClicked={this.handleChildClick}/>
</div>
);
}});
var MySubComponent = React.createClass({
getValue: function () {
return this.props.val;
},
handleOnClick: function (e, value) {
this.props.onSubClicked(e, this.props.val);
},
render: function () {
return (
<div onClick={this.handleOnClick}>{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent />
</div>,
document.getElementById('container')
);