I used this article as an example (React way), but it is not working for me. Please point me to my mistake, as I can't understand what's wrong.
This is the error I see:
Uncaught TypeError: this.props.onClick is not a function
Here is my code:
// PARENT
var SendDocModal = React.createClass({
getInitialState: function() {
return {tagList: []};
},
render: function() {
return (
<div>
{
this.state.tagList.map(function(item) {
return (
<TagItem nameProp={item.Name} idProp={item.Id} onClick={this.HandleRemove}/>
)
})
}
</div>
)
},
HandleRemove: function(c) {
console.log('On REMOVE = ', c);
}
});
// CHILD
var TagItem = React.createClass({
render: function() {
return (
<span className="react-tagsinput-tag">
<span>{this.props.nameProp}</span>
<a className='react-tagsinput-remove' onClick={this.HandleRemove}></a>
</span>
)
},
HandleRemove: function() {
this.props.onClick(this);
}
});
Thanks in advance!
The issue is that this inside the map callback does not refer to the React component, hence this.HandleRemove is undefined.
You can set the this value explicitly by passing a second argument to map:
this.state.tagList.map(function() {...}, this);
Now this inside the callback refers to the same value as this outside the callback, namely the SendDocModal instance.
This has nothing to do with React, it's just how JavaScript works. See How to access the correct `this` context inside a callback? for more info and other solutions.
Try the following:
var SendDocModal = React.createClass({
getInitialState: function() {
var item = {};
item.Name = 'First';
item.Id = 123;
var item2 = {};
item2.Name = 'Second';
item2.Id = 123456;
return {tagList: [item,item2]};
},
HandleRemove: function(c){
console.log('On REMOVE = ', c);
},
render: function() {
return (<div>
{this.state.tagList.map(function(item){
return(
<TagItem nameProp={item.Name} idProp={item.Id} key={item.Id} click={this.HandleRemove}/>
)}, this)}
</div>
)
}
});
// CHILD
var TagItem = React.createClass({
handleClick: function(nameProp)
{
this.props.click(nameProp);
},
render: function(){
return(
<span className="react-tagsinput-tag" ><span onClick={this.handleClick.bind(this, this.props.nameProp)}>{this.props.nameProp}</span><a className='react-tagsinput-remove' ></a></span>
)
}
});
Few changes:
Added 'this' after the tagList mapping. To be honest I am not entirely sure why - perhaps a more experienced programmer can tell us.
Added a key to each TagItem. This is recommended and an the console will inform you that you should do this so that if the state changes, React can track each item accordingly.
The click is passed through the props. See React js - having problems creating a todo list
Related
Box.expandBox(id);
var Box= (function(){
return {
expandBox: function(id) {
console.log('inside expandBox: ' + id);
ReactDOM.render( React.createElement(this.pBox(id)), document.getElementById('activate'))
},
pBox: function(id) {
console.log('inside pBox: '+ id);
return React.createClass({
getInitialState: function(id) {
console.log('inside getInitialState: '+ id);
return {
person_id: id
}
},
........
Trying to assign the state person_id to the id that is being passed externally. I got as far as outputting the data inside pBox but data is lost inside React's child function. I've tried doing the var self = this assignment but to no avail. I'm am lost at the scope when it comes to JS.
Currently, the id is shadowed by the argument of getInitialState(id) and becoming undefined because getInitialState is called without any arguments.
So, remove the argument and you can use the id provided in the pBox method in getInitialState().
pBox: function(id) {
console.log('inside pBox: '+ id);
return React.createClass({
getInitialState: function() {
console.log('inside getInitialState: '+ id);
return {
person_id: id
}
},
The function getInitialState is expecting an argument for id. If it does not receive one, there still is an id in that local scope but it contains undefined. As mentioned by #Shuhei, removing that argument from the function or giving it a different name will allow you to access the id on a higher scope.
For readability's sake, I recommend you separate React.CreateClass into another function.
Your new function would look something like this:
function foo(id){
console.log('inside pBox: '+ id);
return React.CreateClass({...}) //Same code you had before
}
And your code would look something like this:
Box.expandBox(id);
var Box= (function(){
return {
expandBox: function(id) {
console.log('inside expandBox: ' + id);
ReactDOM.render( React.createElement(this.pBox(id)), document.getElementById('activate'))
},
pBox: foo
....
I'm a little weak in javascript.
I'm inspiring myself from this answer to pass a function from parent to child in REACT and I'm having a little difficulty.
Could someone help me correct my code?
Thanks!
var List = React.createClass({
deleting: function(test){
console.log(test);
},
render: function() {
var all = this.props.activities;
var test = List.deleting;
var list = all.map(function(a){
return (<ListItem act={a} del={test}>);
});
return (
<ul> {list}
</ul>
);
}
});
var ListItem = React.createClass({
deleting: function(e){
this.props.del(e.target.parentNode.firstChild.innerHTML);
},
render: function(){
return (
<li key={this.props.act}>{this.props.act}
<div onClick={this.deleting}>X</div>
</li>
);
}
});
The error I get:
You need pass reference to method .deleting that is part of List Object, now you are trying pass var test = List.deleting; that is undefined. In order to this in .map, refers to List, you should set this for .map by yourself - to do that just pass (in our case it should be this because this in render method refers to List) second argument to .map, and pass to del= attribute reference to method this.deleting.
Also set key attribute for ListItem, and in React all tags must be closed - so add /> ( now you are getting error because you have not closed tag ListItem) in the end of ListItem tag
var List = React.createClass({
deleting: function(test) {
console.log(test);
},
render: function() {
var all = this.props.activities;
var list = all.map(function(a) {
return (<ListItem key={a} act={a} del={this.deleting} />);
}, this);
return <ul> {list} </ul>
}
});
Example
I am trying to get a DOM node ref from a dynamic object but I am getting the following error in the Chrome console:
Uncaught TypeError: Cannot read property 'getDOMNode' of undefined.
https://jsfiddle.net/ux4rL8sf/6/
var Hello = React.createClass({
getInitialState: function () {
return {
currentItems : [{"id":"1"} , {"id":"2"} , {"id":"3"}]
};
},
onRowClick: function (i) {
var x;
if (i == 'abs') {
x = this.refs[i].getDOMNode().scrollHeight;
} else {
x = this.refs['row' + i].getDOMNode().scrollHeight;
}
alert(x);
},
render: function() {
var Items = this.state.currentItems.map(function(tv) {
return (<div refs={"row" + tv.id} onClick={this.onRowClick.bind(this, tv.id)} > {tv.id}</div>);
}.bind(this)
);
return (<div> {Items} <div ref="abs" onClick={this.onRowClick.bind(this, 'abs')}>Test </div> </div>) ;
}
}
);
React.render(<Hello />, document.getElementById('container'));
In the callback you provide to map when creating the Items variable in your render method, you have
return (<div refs={...
I think you want
return (<div ref={...
instead.
On line 22 of your fiddle, you use refs instead of ref. I think that should fix your problem.
How to write this without using JSX?
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
This comes from the react.js tutorial: http://facebook.github.io/react/docs/tutorial.html
I know I can do the following:
return (
React.createElement('div', { className: "commentBox" },
React.createElement('h1', {}, "Comments")
)
But this only adds one element. How can I add more next to one another.
You can use the online Babel REPL (https://babeljs.io/repl/) as a quick way to convert little chunks of JSX to the equivalent JavaScript.
var CommentBox = React.createClass({displayName: 'CommentBox',
render: function() {
return (
React.createElement("div", {className: "commentBox"},
React.createElement("h1", null, "Comments"),
React.createElement(CommentList, null),
React.createElement(CommentForm, null)
)
);
}
});
It's also handy for checking what the transpiler outputs for the ES6 transforms it supports.
insin's answer is the direct translation, however you may prefer to use factories.
var div = React.createFactory('div'), h1 = React.createFactory('h1');
var CommentBox = React.createClass({displayName: 'CommentBox',
render: function() {
return (
div({className: "commentBox"},
h1(null, "Comments"),
React.createElement(CommentList, null),
React.createElement(CommentForm, null)
)
);
}
});
createFactory essentially partially applies createElement. So the following are equivalent:
React.createElement(c, props, child1, child2);
React.createFactory(c)(props, child1, child2);
If you're just using es6 but aren't fond of JSX you can make it less verbose with destructuring assignment. See this jsbin for an interactive example using 6to5 instead of jsx.
var [div, h1, commentForm, commentList] = [
'div', 'h1', CommentForm, CommentList
].map(React.createFactory);
if you have a variable number of children then you can use that:
Using apply function which take an array of parameters.
React.createElement.apply(this, ['tbody', {your setting}].concat(this.renderLineList()))
where renderLineList is for instance:
renderLineList: function() {
var data=this.props.data;
var lineList=[];
data.map(function(line) {
lineList.push(React.createElement('tr', {your setting}));
});
return lineList;
}
You just add them one after another as children to your parent component,
return React.createElement("div", null,
React.createElement(CommentList, null),
React.createElement(CommentForm, null)
);
I had this problem, it took a while to solve by stepping through the interpreter source code:
var arrayOfData = [];
var buildArray = (function () {
var id;
var name;
return{
buildProc(index, oneName){
id = index;
name = oneName;
arrayOfData[index] = (React.createElement('Option', {id},name));
}
}
})();
// then
this.state.items = result;
var response = parseJson.parseStart(this.state.items);
var serverDims = response.split(":");
for (var i = 1; i < serverDims.length; i++) {
buildArray.buildProc(i, serverDims[i] )
}
// then
render(){
const data = this.props.arrayOfData;
return (
React.createElement("select", {},
data
)
// {data} Failed with "object not a valid React child, data with no curly's worked
)
}
I have the following:
// Child Array is Cards, trying to add computed observable for each child
var CardViewModel = function (data) {
ko.mapping.fromJS(data, {}, this);
this.editing = ko.observable(false);
};
var mapping = {
'cards': { // This never gets hit, UNLESS I remove the 'create' method below
create: function (options) {
debugger;
return new CardViewModel(options.data);
}
},
create: function(options) {
var innerModel = ko.mapping.fromJS(options.data);
innerModel.cardCount = ko.computed(function () {
return innerModel.cards().length;
});
return innerModel;
}
};
var SetViewModel = ko.mapping.fromJS(setData, mapping);
debugger;
ko.applyBindings(SetViewModel);
However I can't get the 'cards' binding to work - that code isn't reached unless I remove the 'create' method. I'm trying to follow the example from the knockout site:
http://knockoutjs.com/documentation/plugins-mapping.html
They do this for the child object definition:
var mapping = {
'children': {
create: function(options) {
return new myChildModel(options.data);
}
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
With the ChildModel defined like this:
var myChildModel = function(data) {
ko.mapping.fromJS(data, {}, this);
this.nameLength = ko.computed(function() {
return this.name().length;
}, this);
}
I've spent the past day on this and cannot for the life of me figure out why this isn't working. Any tips would be awesome.
EDIT: Here's a fiddle of what I'm working with. It's only showing SIDE 1 in the result because "editing" isn't recognized here:
<div data-bind="visible: !$parent.editing()" class="span5 side-study-box">
http://jsfiddle.net/PTSkR/1/
This is the error I get in chrome when I run it:
Uncaught Error: Unable to parse bindings. Message: TypeError: Object
has no method 'editing'; Bindings value: visible: !$parent.editing()
You have overridden the create behavior for your view model. The mapping plugin will not call any of the other handlers for the properties for you. Since you're mapping from within the create method, move your cards handler in there.
var mapping = {
create: function(options) {
var innerModel = ko.mapping.fromJS(options.data, {
'cards': {
create: function (options) {
debugger;
return new CardViewModel(options.data);
}
}
});
innerModel.cardCount = ko.computed(function () {
return innerModel.cards().length;
});
return innerModel;
}
};
updated fiddle
you didnt needed to have parenthesis. I just changed from
!$parent.editing()
to
!$parent.editing
See the updated fiddle here