How do I handle complex objects in ReactJS? - javascript
I have the following ReactJS code :
var data1 = {"Columns":["Title1","Title2","Title3"],"Rows":[{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
if(this.props.data){
}
return (
<div>Text</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
<GridRow data={columns}>
);
});
var Row = this.props.data.Rows.map(function (rows) {
return (
<GridRow data={rows}>
);
});
}
return (
<ul>
<li>{Header}</li>
<li>{Row}</li>
</ul>
);
}
});
var GridBox = React.createClass({
render: function(){
return (
<GridList data={data1} />
);
}
});
I'm trying to pass the data1 variable to the GridList where it is split up to Columns (for header) and rows. The problem is that I get the following exception at runtime:
In file "~/Scripts/Grid.jsx": Parse Error: Line 30: Unexpected token
return (at line 30 column 6) Line: 52 Column:3
I'm running this from within Visual Studio 2013 with ReactJS.
The stated Line nr and colum makes no sense
Im trying to render a table based on metadata(columns) and row data from service.
You need to close tags either with a matching closing tag, or using self closing tags.
// ERROR
<GridRow data={rows}>
// OK
<GridRow data={rows}></GridRow>
// Best
<GridRow data={rows} />
The error message isn't very helpful.
Also, when creating an array of nodes, it's good to give them keys.
Rows.map(function(row, i){
return <GridRow data={rows} key={i} />;
});
I played around with it some more, and the weirdness comes from JSX accepting anything between an opening tag and <, {, or } as raw text. If you did something like this:
var GridList = React.createClass({
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
<GridRow data={columns}>
);
});
var Row = this.props.data.Rows.map(function (rows) </GridRow>
)});
}
return (
<ul>
<li>{Header}</li>
<li>{Row}</li>
</ul>
);
}
});
It'll happily output this:
var GridList = React.createClass({displayName: "GridList",
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
React.createElement(GridRow, {data: columns},
");" + ' ' +
"});" + ' ' +
"var Row = this.props.data.Rows.map(function (rows) ")
)});
}
return (
React.createElement("ul", null,
React.createElement("li", null, Header),
React.createElement("li", null, Row)
)
);
}
});
It's completely content until it encounters the { after Rows.map(function (rows), which means "go back into JavaScript expression mode", and it encounters a return in an expression, which is invalid, so it bails, and gives the best error it can.
Related
React Redux only displaying one element
I'm new to React and am having some trouble getting it to work. I have a react class that puts a bunch of JSON in the store as an object, a PushNotification with two elements: pushId and count. So, the store should have a list of PushNotifications. However, when I try and display that information to the screen, it only outputs one of them. My React code is: socket.onmessage = function(event) { console.log("Received message" + event.data.toString()); store.dispatch(receivedPushNotification(event.data)); }; var App = React.createClass({ render: function () { var pushNotifications = _.map(this.props.pushNotifications, function(value, key, notification) { var percentage = (notification.count / 50) * 100; return ( <div className="row" key={notification.pushid}> <div className="col-sm-12"> <Card> <h1 className="marB15">{notification.pushid}</h1> <div className="clearfix"> <div className="progress progress-striped active marB10"> <div className="progress-bar" style={{'width': percentage + '%'}}></div> </div> <div className="pull-right"> <p>Total: {notification.count}</p> </div> </div> </Card> </div> </div> ) }); } }); My Reducer is: var pushNotificationDefaultState = {}; var pushNotificationReducer = function(state, action) { switch(action.type) { case 'RECEIVED_PUSH_NOTIFICATION': var obj = JSON.parse(action.PushNotification); console.log(obj.pushid); console.log(obj.count); return obj; default: if (typeof state === 'undefined') { return pushNotificationDefaultState; } return state; } }; module.exports = Redux.combineReducers({ pushNotifications: pushNotificationReducer }); Thanks in advance,
The problem is, that you are storing only one notification in redux state. Instead of this, you should store an array of them. // Using an emty array as default state, instead of object. var pushNotificationDefaultState = []; var pushNotificationReducer = function(state, action) { switch(action.type) { case 'RECEIVED_PUSH_NOTIFICATION': var obj = JSON.parse(action.PushNotification); // Returning new array, which contains previous state and new notification. return [].concat(state, [obj]); default: if (typeof state === 'undefined') { return pushNotificationDefaultState; } return state; } }; module.exports = Redux.combineReducers({ pushNotifications: pushNotificationReducer }); Also, you are not returning notifications elements from render function: socket.onmessage = function(event) { console.log("Received message" + event.data.toString()); store.dispatch(receivedPushNotification(event.data)); }; var App = React.createClass({ render: function () { // To render notifications, return it array from render function return _.map(this.props.pushNotifications, function(value, key, notification) { var percentage = (notification.count / 50) * 100; return ( <div className="row" key={notification.pushid}> <div className="col-sm-12"> <Card> <h1 className="marB15">{notification.pushid}</h1> <div className="clearfix"> <div className="progress progress-striped active marB10"> <div className="progress-bar" style={{'width': percentage + '%'}}></div> </div> <div className="pull-right"> <p>Total: {notification.count}</p> </div> </div> </Card> </div> </div> ) }); } });
add return statement in your render, after map return (<div>{pushNotifications}</div>); in reducer you should add new notif in array case 'RECEIVED_PUSH_NOTIFICATION': var notif = JSON.parse(action.PushNotification); return [...state, notif ];
Pull data from IndexedDB into array and output it via ReactJS
My actual Javascript code is the following: var schoolsData = new Array(); myDB.schools .each(function(school) { console.log('"' + school.title + '" wird auf den Array gepusht.'); schoolsData.push(new Array(school.title, schools.schoolnumber, school.address, school.principal, school.email, school.creationdate, school.lastupdate, school.comment)); }); var SchoolsRender = React.createClass({ render: function() { return ( <tr> {this.props.list.map(function(listValue){ return <td>{listValue}</td>; })} </tr> ) } }); ReactDOM.render(<SchoolsRender list={schoolsData} />, document.getElementById('schoolsDATA')); As you can see I am trying to pull data from my local IndexedDB database (I am using dexieJS) and put it via ReactJS into a table element but nothing appears. Where is the point? Edit: I think the problem is basically that I'm trying to output that 3D array. Is there any simple and elegant solution?
Add another component RowRender to render single row. Modify SchoolsRender component accordingly. var RowRender = React.createClass({ render: function() { return ( <tr> <td>{this.props.title}</td> <td>{this.props.schoolnumber}</td> <td>{this.props.address}</td> <td>{this.props.principal}</td> </tr> ) } }); var SchoolsRender = React.createClass({ render: function() { return ( <table> {this.props.list.map(function(listValue,index){ return <RowRender key={index} title={listValue.title} schoolnumber={listValue.schoolnumber} address={listValue.address} title={listValue.address} />; })} </table> ) } });
How to turn objects in to HTML
How do i get this : <li> <div class='myClass1'>myData1</div> <div class='myClass2'>myData2</div> <div class='myClass3'>myData3</div> <div class='myClass4'>myData4</div> </li> from this code var data1 = {"Columns":[{"Title":"Title1","HTMLClass":"g1_Title"},{"Title":"Title2","HTMLClass":"g2_Title"},{"Title":"Title3","HTMLClass":"g3_Title"}],"Rows":[{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]}]}; var GridRow = React.createClass({ render: function() { var data = [], columns; // puts all the data in to a better structure (ideally the props would have this structure or this manipulation would be done on onReceiveProps) if(this.props.columns){ for(var ii = 0; ii < this.props.columns.length; ii++){ data.push({ class: this.props.columns[i].HTMLClass, contents: this.props.Cell[i] }) } } // Its best to map JSX elements and not store them in arrays columns = data.map(function(col) { return <div className= + {col.class}> {col.contents} </div>; }); return ( <div> <li> {columns} </li> </div> ); } }); var GridHead = React.createClass({ render: function() { if(this.props.data){ var cell = this.props.data.Title; var htmlClass = this.props.data.HTMLClass; } return ( <div className={htmlClass}>{cell}</div> ); } }); var GridList = React.createClass({ render: function() { if(this.props.data){ var header = this.props.data.Columns.map(function (columns) { return ( <GridHead data={columns} /> ); }); var row = this.props.data.Rows.map(function (row, i) { return ( <GridRow columns={data1.Columns} cells={row.Cells} key={i} /> ); }); } return ( <ul> <li>{header}</li> {row} </ul> ); } }); var GridBox = React.createClass({ render: function(){ return ( <GridList data={data1} /> ); } }); The output right now is this In file "~/Scripts/Grid.jsx": Parse Error: Line 26: XJS value should be either an expression or a quoted XJS text (at line 26 column 35) Line: 52 Column:3
As your question initially asked was to do with just the GridRow component and nothing else I have not touched any other component. Your main problem was you were assigning className = + //something in your GridRow component which isn't the correct way to assign. There were other errors like missing div tags. Better GridRow When the component mounts a columndata variable is created and is populated with formatted data using formatData();. I do not recommend you do data formatting in this component (although it is doable). You should either format your data at a top level component and pass down formatted data or accept data in the correct structure. My GridRow component to this: var GridRow = React.createClass({ componentWillMount: function() { this.columndata = []; this.formatData(); }, formatData: function() { // Formats prop data into something manageable if (this.props.columns && this.props.cells) { for(var ii = 0; ii < this.props.columns.length; ii++){ this.columndata.push({ class: this.props.columns[ii].HTMLClass, contents: this.props.cells[ii] }) } this.forceUpdate(); // Forces a rerender } }, componentDidUpdate: function(prevProps, prevState) { // If this component receives the props late if (!prevProps.cells && !prevProps.columns) { this.formatData(); } }, render: function() { var columns; // Its best to map JSX elements and not store them in arrays columns = this.columndata.map(function(col) { return <div className={col.class}> {col.contents} </div>; }); return ( <div> <li> {columns} </li> </div> ); } }); I think it's important to note that you should avoid storing JSX elements in arrays. I think you were basically on the money, except you were missing classname and div tags.
React.js this.props.data.map() is not a function
I'm messing around with react and trying to parse and render a json object. Right now, I'm just setting it with a hard-coded object for testing and not getting it from an ajax call. <script type="text/jsx"> var Person = React.createClass({ render: function() { return ( <div> <p className="personName"></p> <p className="personSA1"></p> <p className="personSA2"></p> <p className="zip"></p> <p className="state"></p> <p className="country"></p> </div> ); } }); var PersonDiv = React.createClass({ render: function() { var personNodes = this.props.data.map(function(personData){ return ( <Person personName={personData.person.firstname} personSA1={personData.person.street1} personSA2={personData.person.street2} zip={personData.person.zip} state={personData.person.state} country={personData.person.country}> </Person> ) }); return ( <div> {personNodes} </div> ); } }); React.render( <PersonDiv data={data} />, document.getElementById('jsonData') ); I'm setting the data variable with <script> var data = "[" + '<%=data%>' + "]"; </script> The data object is one that I'm creating on the java side of a portlet. I know that the json is valid, because I can use JSON.parse(json) to parse and traverse the string, but I keep getting that map() is not a function.
It appears that your data is not a json object, it is a string. You probably need to run data = JSON.parse(data); to convert your data into an actual javascript object to be able to use it. An easy test for this would be to run <script> var data = "[" + '<%=data%>' + "]"; console.log(data); console.log(JSON.parse(data)); </script> You should notice the difference.
You are passing result of console.log as first parameter to React.render: React.render( console.log("inside render"), <PersonDiv data={data} />, document.getElementById('jsonData') ); It should be like: console.log("will render"); React.render( <PersonDiv data={data} />, document.getElementById('jsonData') );
CasperJS querySelectorAll + map.call
html file <table id="tbl_proxy_list"> ........... <tr> ...... <td align="left"> <time class="icon icon-check">1 min</time> </td> <td align="left"> <div class="progress-bar" data-value="75" title="4625"></div> </td> </tr> </table> ip.js file casper.start('http://www.proxynova.com/proxy-server-list/', function() { var info_text = this.evaluate(function() { var nodes = document.querySelectorAll('table[id="tbl_proxy_list"] tr'); return [].map.call(nodes, function(node) { //return node.innerText; return node; }); }); var tr_data = info_text.map(function(str) { var elements = str; var data = { ip : elements, port : elements[1], lastcheck : elements[2], speed : elements[3], // <== value is 75.. }; return data; }); utils.dump(tr_data); }); casper.run(); return node.innerText is only text. ip is a text value port is a text value lastcheck is a text value speed is not a text value (data-value="75") I want to import data-value="75" (speed value is 75). I do not know what to do. ======================================== It's work.. good. thank you Artjom. but tr_data echo error. first, you code modify.. return { "ip": tr.children[0].innerText.trim(), "port": tr.children[1].innerText.trim(), "lastcheck": tr.children[2].innerText.trim(), "speed": tr.children[3].children[0].getAttribute("data-value") }; and echo.. //this.echo(tr_data.length); for(var ii=0; ii<tr_data.length; ii++) { this.echo(tr_data[ii]['ip']); } at run, blow error.. TypeError: 'null' is not an object (evaluating 'tr_data.length'); what is problem? I need your help.. thanks.
You cannot pass DOM elements from the page context (inside evaluate callback). From the docs: Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine. Returning an array of DOM elements will result in an array of as many undefined values. That means you need to map everything inside the page context and then return the resulting array. You also need only one map. var tr_data = this.evaluate(function() { var nodes = document.querySelectorAll('table[id="tbl_proxy_list"] tbody tr'); return Array.prototype.map.call(nodes, function(tr, i) { if (tr.children.length != 6) { return null; // skip ads } return { ip: tr.children[0].innerText.trim(), port: tr.children[1].innerText.trim(), lastcheck: tr.children[2].innerText.trim(), speed: tr.children[3].children[0].getAttribute("data-value") }; }).filter(function(data){ return data !== null; // filter the null out });; }); You also might want to trim the excess white space.