React unexpected token { } when trying to loop inside render - javascript

I was following the example on the react website on how to loop inside render I've got what I wanted to accomplish done, but I have
var AdminForumContainer = React.createClass({
getInitialState: function() {
return { containers: [] }
},
componentDidMount: function() {
$.ajax({
method: 'GET',
url: '/admin/manage/forum/populate',
success: function(data) {
console.log(data);
}
})
},
render: function() {
return (
{this.state.containers.map(function(container) {
return (
<table className="containers">
<caption>{container.containername}</caption>
<thead className="containerTitle">
<tr>
<td colspan="2">Main Threads</td>
</tr>
</thead>
<thead>
<tr>
<td>Thread Name</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{
container.mainthreads.map(function(mainthread) {
return (
<tr>
<td>{mainthread.threadname}</td>
<td><button className="button alert">Delete</button></td>
</tr>
)
})
}
<tr>
<td><input type="text"/></td>
<td><button className="button" onclick={this.createMainThread(container.containerid)}>Create</button></td>
</tr>
</tbody>
<thead>
<tr>
<td colspan="2">Sub Threads</td>
</tr>
</thead>
<tbody>
{
container.mainthreads.map(function(subthread) {
return (<tr>
<td>{subthread.threadname}</td>
<td><button className="button alert">Delete</button></td>
</tr>)
})
}
<tr>
<td><input type="text"/></td>
<td><button className="button" onclick={this.createSubThread(container.containerid)}>Create</button></td>
</tr>
</tbody>
</table>
)
})}
)
}
});
but I get
Uncaught SyntaxError: http://localhost:8080/static/js/admin.jsx: Unexpected token (16:8)
14 | render: function() {
15 | return (
> 16 | {this.state.containers.map(function(container) {
| ^
17 |
18 | <table className="containers">
19 | <caption>{container.containername}</caption>
not sure what is wrong here. Thanks.

That line looks OK. It's the next line that's an issue. Your loop function needs a return statement. i.e.
{this.state.containers.map(function(container) {
return (
<table className="containers">
Same goes for the other functions passed to Array#map.
UPDATE: I've got it. Remove the surrounding braces. They're only needed inside a JSX container. i.e.
UPDATE Mk II: In fact you need a container since React components must have a single root element. So put the whole thing in a wrapper div. i.e.
render: function () {
return (
<div>
{this.state.containers.map(function(container) {

Related

Suggestion to refactor code into simple way -React

I have multiple with the same class name and method with different parameter
i want to refactor the below code to a simpler way any suggestion would be helpful.
<table class="greyGridTable">
<tbody>
<tr>
<td>AA</td>
<td className = 'table-container'>{formatDate(someMethod1(param1,a)}</td>
</tr>
<tr>
<td>BB</td>
<td className = 'table-container'>{formatDate(someMethod1(param1,b)}</td>
</tr>
<tr>
<td>CC</td>
<td className = 'table-container'>{formatDate(someMethod1(param1,c)}</td>
</tr>
</tbody>
</table>
I want to refactor the code with the same component.
I hope this would be helpful. thanks
export const TableItems = ({data}) => {
return (
<>
{data.map(item => (
<tr>
<td>{item.name}</td>
<td className='table-container'> {item?.symbol} {formatDate(someMethod1(param1,a)}</td>
</tr>
))}
</>
)
}
const data = [
{
name: AA,
},
{
name: BB,
},
{
name: CC,
symbol: '£'
}
]
<table class="greyGridTable">
<tbody>
<TableItems data={data} />
</tbody>
</table>
Try to use an array instead. Having reusable components in an array is easy to maintain, expand and read. Definitely a good practice.
let tableItems = [{name:"AA",date:new Date()},.....]
return (
<table class="greyGridTable">
<tbody>
{tableItems.map((item,index)=>{
return(
<tr>
<td>{item.date}</td>
<td className = 'table-container'>{item.date}</td>
</tr>
)
})}
</tbody>
</table>
)

React JS - How do I optimize render code?

I have this code in my render function, but I have 5 different versions with minor html changes. I made new tables with each of the 5. How would I optimize it so I do not have to repeat a lot of html/js code?
<table>
<thead>
<tr>
<th></th>
<th className='desc-col'>Description</th>
<th className='button-col'>Amount</th>
</tr>
</thead>
<tbody> { this.showData
this.state.data.map((exp) => {
if (exp.typeOfItem === "Asset" && exp.term == "Short-Term" ) {
return <tr>
<td className='counterCell'></td>
<td className='desc-col'>{exp.description}</td>
<td className='button-col'>${exp.amount}</td>
<td className='button-col'>
<Update expense={exp} />
</td>
<td className='button-col'>
<Delete expense={exp} />
</td>
</tr>
}
})
}
</tbody>
</table>
<table>
<thead>
<tr>
<th></th>
<th className='desc-col'>Description</th>
<th className='button-col'>Amount</th>
</tr>
</thead>
<tbody>
{
this.state.data.map((exp) => {
if (exp.typeOfItem === "Asset" && exp.term == "Long-Term" ) {
return <tr>
<td className='counterCell'></td>
<td className='desc-col'>{exp.description}</td>
<td className='button-col'>${exp.amount}</td>
<td className='button-col'>
<Update expense={exp} />
</td>
<td className='button-col'>
<Delete expense={exp} />
</td>
</tr>
}
})
}
</tbody>
</table>
You can pull out your Table in a custom component and pass down the data as props,
Your new component would be,
import React from 'react'
const TableComponent = (props) => (
<table>
<thead>
<tr>
<th></th><th className='desc-col'>Description</th>
<th className='button-col'>Amount</th>
</tr>
</thead>
<tbody>
{
props.data.map((exp) => {
if (exp.typeOfItem === props.typeOfItem && exp.term === props.term ) {
return <tr>
<td className='counterCell'></td>
<td className='desc-col'>{exp.description}</td>
<td className='button-col'>${exp.amount}</td>
<td className='button-col'> <Update expense={exp}/></td>
<td className='button-col'><Delete expense={exp} /></td>
</tr>
}
})
}
</tbody>
</table>
)
export default TableComponent
Now you can render this component by passing props,
<TableComponent data={this.state.data} typeOfItem="Asset" term="Short-Term"/>
<TableComponent data={this.state.data} typeOfItem="Asset" term="Long-Term"/>
Note: If you have any other variable's to be used in Table, do pass them as props and in your TableComponent use them appropriately.
You would be better off splitting the array before the render.
for instance:
const assets = this.state.data.filter(item => item.typeOfItem === "Asset");
const longTerm = [];
const shortTerm = [];
assets.forEach((asset) => {
asset.term = "long" ? longTerm.push(asset) : shortTerm.push(asset);
});
Next you can render it with a component you want
longTerm.map(asset => {
return <MyComponent amount={asset.amount} ... />
})
shortTerm.map(asset => {
return <MyComponent amount={asset.amount} ... />
})
And your component could be
function MyComponent(props) {
return <tr>
<td className='counterCell'></td>
<td className='desc-col'>{props.description}</td>
//rest
</tr>
}
additionally you could make a table component and pass it the collection which calls MyComponent
function TableComponent({collection}) {
return <table>
<thead>
<tr>
<th></th><th className='desc-col'>Description</th>
<th className='button-col'>Amount</th>
</tr>
</thead>
<tbody>
{
collection.map(asset => {
return <MyComponent ....
});
}
</tbody>
</table>
}
and then the initial render would just be
<>
<TableComponent collection={longterm} />
<TableComponent collection={shortterm} />
</>

Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>

I'm creating a table in React (I'm new to React), but the CategoryDataCan we live with those or should we use something else? It is not creating cells correctly, i.e. it's creating cells that are not aligned with the <th> that come from the parent and they do not have cell borders at all. It's also giving these warnings:
Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>. See Param > tbody > Row > div.
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. See Row > div > tr.
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. See CategoryData > div > tr.
I'm not sure why these warnings are happening and why the table cells(from CategoryData) are not getting aligned and don't have cell borders. What's the correct way to do it?
Code
var Param = React.createClass({
getInitialState: function() {
return {
isLoading: true,
panelTitle: "",
data: [],
categories: []
}
},
updateState: function() {
var that = this;
var categories = new Set();
rest.getParameters('service').then(function (results) {
for (var i = 0; i < results.data.length; i++) {
categories.add(results.data[i].category);
}
that.setState({
data: results.data,
categories: Array.from(categories)
})
}).catch(function (err) {
console.error('rest.getParameters(): ', err);
that.setState({
isLoading: true,
data: [],
categories: []
})
});
},
componentDidMount: function() {
this.updateState();
},
render: function() {
return (
<Panel className="panel-info" header={<h4 className="panel-title">Service Config</h4>}>
<div>
<table className="table table-striped table-bordered table-hover table-condensed table-responsive">
<thead>
<tr>
<th className="col-lg-2 text-center">AMP Name</th>
<th className="col-lg-2 text-center">Athena Name</th>
<th className="col-lg-2 text-center">Description</th>
<th className="col-lg-1 text-center">Default</th>
<th className="col-lg-1 text-center">Min Value</th>
<th className="col-lg-1 text-center">Max Value</th>
<th className="col-lg-1 text-center">Action</th>
</tr>
</thead>
<tbody>
{this.state.categories.map((category, index) =>
<th colSpan="7" key={index} style={{'textAlign':'left', 'paddingLeft':'5px', 'backgroundColor': '#D3D0CF'}}>{this.state.category}</th>
this.state.data.map((row, i) =>
if (row.category === category) {
<tr key={i}>
<td className="col-lg-2 text-center">{row.name}</td>
<td className="col-lg-2 text-center">{row.alias}</td>
<td className="col-lg-2 text-center">{row.description}</td>
<td className="col-lg-1 text-center">{row.default_value}</td>
<td className="col-lg-1 text-center">{row.min_value}</td>
<td className="col-lg-1 text-center">{row.max_value}</td>
<td className="col-lg-1 text-center">Action</td>
</tr>
}
)
)}
</tbody>
</table>
</div>
</Panel>
);
}
});
I would change the 'th' to a 'tr' because I'm pretty sure react will give you a warning if you add 'th' inside 'tbody'
let finalList = []
this.state.categories.forEach( (cat, index) => {
finalList.push(<tr...>{this.state.category}</tr>)
this.state.data.forEach( (row, index) => {
if(row.category === cat){
finalList.push(
<tr key={i}>
<td className="col-lg-2 text-center">{row.name}</td>
<td className="col-lg-2 text-center">{row.alias}</td>
<td className="col-lg-2 text-center">{row.description}</td>
<td className="col-lg-1 text-center">{row.default_value}</td>
<td className="col-lg-1 text-center">{row.min_value}</td>
<td className="col-lg-1 text-center">{row.max_value}</td>
<td className="col-lg-1 text-center">Action</td>
</tr>
)
}
})
})
Word of warning I would avoid using tables checkout css grids their a lot more flexible and pretty well supported
EDIT: From version 16.0.0 onwards in react, you could make use of React.Fragment to return multiple elements from render
<tbody>
{
this.state.categories.map((category, index) => {
var innerData = this.state.data.map((row, i) => {
if (row.category === category) {
return (
<tr key={i}>
<td className="col-lg-2 text-center">{row.name}</td>
<td className="col-lg-2 text-center">{row.alias}</td>
<td className="col-lg-2 text-center">{row.description}</td>
<td className="col-lg-1 text-center">{row.default_value}</td>
<td className="col-lg-1 text-center">{row.min_value}</td>
<td className="col-lg-1 text-center">{row.max_value}</td>
<td className="col-lg-1 text-center">Action</td>
</tr>
)
}
return null
})
return (
<React.Fragment>
<th colSpan="7" key={index} style={{
'textAlign': 'left',
'paddingLeft': '5px',
'backgroundColor': '#D3D0CF'
}}>{this.state.category}</th>,
{innerData}
</React.Fragment>
)
})
}
</tbody>
Before v16
With the help of JSX syntactic sugar it is possible to return multiple elements from within a component, by writing them as comma separated elements in an array like
<tbody>
{
this.state.categories.map((category, index) => {
var innerData = this.state.data.map((row, i) => {
if (row.category === category) {
return (
<tr key={i}>
<td className="col-lg-2 text-center">{row.name}</td>
<td className="col-lg-2 text-center">{row.alias}</td>
<td className="col-lg-2 text-center">{row.description}</td>
<td className="col-lg-1 text-center">{row.default_value}</td>
<td className="col-lg-1 text-center">{row.min_value}</td>
<td className="col-lg-1 text-center">{row.max_value}</td>
<td className="col-lg-1 text-center">Action</td>
</tr>
)
}
return null
})
return ([
<th colSpan="7" key={index} style={{
'textAlign': 'left',
'paddingLeft': '5px',
'backgroundColor': '#D3D0CF'
}}>{this.state.category}</th>,
[...innerData]
])
})
}
</tbody>
Also when you make use of if statements within a map function, you need to have them outside of the return statement, now if you do {this.state.categories.map((category, index) => <tr>... it means that whatever is after the arrow is considered to be part of the return and hence you inner map's if statement will give you an error.
There is an issue on react github page for returning multiple elements. Read through it for more details.

React : Render returns createElement

I just started to learn React and I'm using it with a Rails backend.
In my view I have :
<%= react_component 'Products', { data: #products } %>
It works fine with this static code :
var Products = React.createClass({
getInitialState: function () {
return {products: this.props.data};
},
getDefaultProps: function() {
return {products: []};
},
render: function () {
return (
<div className="products">
<h2 className="title">List of products</h2>
<table className="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Company</th>
<th>RRP</th>
</tr>
</thead>
<tbody>
<tr>
<td>AAA</td>
<td>BBB</td>
<td>CCC</td>
</tr>
</tbody>
</table>
</div>
);
}
});
I've got my table displayed well.
The next step is to have the same result but with each line representing a new product's element. So I start to create a new React Class in the same file :
var ProductLine = React.createClass({
render: function () {
return (
<tr>
<td>AAA</td>
<td>BBB</td>
<td>CCC</td>
</tr>
);
}
});
My problem is, how can I render this ProductLine in my table ? Because if I do this :
<tbody>
React.createElement ProductLine
</tbody>
The line is considered as plain text and not rendered...
Actually I found the solution just after posting this question.
This post called Thinking in React from Pete Hunt is very useful, especially for a React newbie. Also, the example is almost the same as my situation...
var ProductRow = React.createClass({
render: function () {
return (
<tr>
<td>{this.props.product.name}</td>
<td>{this.props.product.company_id}</td>
<td>{this.props.product.price}</td>
</tr>
);
}
});
var ProductTable = React.createClass({
render: function () {
var rows = [];
this.props.data.forEach(function(product) {
rows.push(<ProductRow product={product} key={product.id} />);
});
return (
<div className="products">
<h2 className="title">List of products</h2>
<table className="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Company</th>
<th>RRP</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
});
I could be wrong but <ProductLine /> is how you would instantiate a component within the render function of another parent component i.e.:
<tbody>
<ProductLine />
</tbody>

How to set className for selected child component in ReactJS

New to ReactJs - I have had a look at the documentation here and here but I am a bit confused.
So I have a component that creates several table rows according to the JSON data.
I am trying to make it so once a radio button is selected, the class of the parent <td> is set to 'success'. But at the moment all the rows with that column get the same class name.
var SearchResult = React.createClass({
getInitialState: function () {
return {
site: '',
address: '',
data: [],
checked: ''
};
},
onSiteChanged: function (e) {
this.setState({
site: e.currentTarget.value,
checked: 'success'
});
},
render: function () {
var resultRows = this.props.data.map(function (result) {
return (
<tr>
<td className={this.state.checked}>
<input type="radio" name="site_name"
value={result.SITE_NAME}
onChange={this.onSiteChanged}
key={result.id}/>{result.SITE_NAME}</td>
<td>
<input type="radio" name="address"
value={result.ADDRESS}
onChange={this.onAddressChanged} />{result.ADDRESS}</td>
</tr>
);
}, this);
return (
<table className="table table-hover table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{resultRows}
</tbody>
<tfoot>
<tr>
<td></td>
<td>{this.state.site}</td>
<td>{this.state.address}</td>
</tr>
</tfoot>
</table>
);
}
});
What is the best ReactJS way to proceed to make sure the selected result get the selected class name?
Thank you.
To modify values passed to classSet property React has special addon: React.addons.classSet. It is very handy when you are changing multiple different classes but in your case it can be usefull, too:
var SearchResult = React.createClass({
getInitialState: function () {
return {
site: '',
address: '',
checked: false,
data: [],
};
},
onSiteChanged: function (selected) {
this.setState({
site: selected.SITE_NAME,
checked: selected.id,
});
},
render: function () {
var resultRows = this.props.data.map(function (result) {
var cx = React.addons.classSet({
success: (this.state.checked === result.id)
});
return (
<tr key={result.id}>
<td className={cx}>
<input type="radio" name="site_name"
value={result.SITE_NAME}
onChange={this.onSiteChanged.bind(this, result)}
/>{result.SITE_NAME}</td>
<td>
<input type="radio" name="address"
value={result.ADDRESS}
onChange={this.onAddressChanged} />{result.ADDRESS}</td>
</tr>
);
}, this);
return (
<table className="table table-hover table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{resultRows}
</tbody>
<tfoot>
<tr>
<td></td>
<td>{this.state.site}</td>
<td>{this.state.address}</td>
</tr>
</tfoot>
</table>
);
}
});

Categories