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>
Related
I am trying to build one table using props, and pass the value from that table to another function. But for some reason, the result is not displaying. What have I done wrong?
import Table from "https://cdn.skypack.dev/react-bootstrap#2.5.0";
function Tables(props) {
return (
<Table>
<thead>
<tr>
<th>a</th>
<th>b</th>
<th>c</th>
</tr>
</thead>
<tbody>
<tr>
<td>{props.first}</td>
<td>{props.second}</td>
<td>{props.third}</td>
</tr>
</tbody>
</Table>
)
}
function App() {
return (
<div>
<Tables first="Sara" />
<Tables second="Cahal" />
<Tables third="Edite" />
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
You are rendering the table three times, each one with only one prop, you need to
If you want to show one table with the three props, it should be like that.
import Table from "https://cdn.skypack.dev/react-bootstrap#2.5.0";
function Tables(props) {
return (
<Table>
<thead>
<tr>
<th>a</th>
<th>b</th>
<th>c</th>
</tr>
</thead>
<tbody>
<tr>
<td>{props.first}</td>
<td>{props.second}</td>
<td>{props.third}</td>
</tr>
</tbody>
</Table>
)
}
function App() {
return (
<div>
<Tables first="Sara" second="Cahal" third="Edite" />
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
I have an array of results I'm wanting to render in a table:
const ScanOutput = () => (
<div class="results">
<h1>
<b>Scan Results</b>
</h1>
<h3>{results.length} results returned</h3>
<table class="styled-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{results.map((result) => (
<tr>
<td>{result.id}</td>
<td>{result.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
This works, but I'd like to add some extra features - for example, if no results are returned (e.g. results.length == 0) I'd like to render "No results found" instead of an empty table. Furthermore, I'd also like to make it so that with this section here:
<h3>{results.length} results returned</h3>
If only 1 result is returned it renders this instead:
<h3>{results.length} result returned</h3>
What's the cleanest way to do this in React?
You can render conditionally.
<div class="results">
{result?.length > 0 ?
<>
<h1>
<b>Scan Results</b>
</h1>
<h3>{results.length} results returned</h3>
<table class="styled-table">
{...}
</table>
</>
:
<h1>No results found</h1>
}
</div>
You can create a new component for the results table
const ResultsTable = ({ results }) => {
if (results?.length === 0) return <h3>No results found</h3>;
if (results?.length === 1) return ...;
return (
<table class="styled-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{results.map((result) => (
<tr>
<td>{result.id}</td>
<td>{result.name}</td>
</tr>
))}
</tbody>
</table>
)
}
I am trying to render my dynamic JSON , but I am getting a table per result when it should be rendered in the same table.. what's happening this? my dummy array is a model from my backend api
const arr = [
{
"Demo": [
{
"_id": "T08210",
"name": "tilehaha",
"tags": [
"Demo"
],
"queries": [],
"urls": [],
"max_leght": [],
"count_items": []
},
],
}
];
export default function Demo() {
return (
<div>
{arr.map(obj =>
Object.entries(obj).map(([key, value]) => (
<table class="table">
<thead>
<tr key={key}>
<th scope="col">{key}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{value.activityType}</td>
<td>{value.durationInSeconds}</td>
</tr>
</tbody>
</table>
)))}
</div>
);
}
I need to place in the blue section as heading 2671161009, and 2671161249 , and their child items as the following
Try this.
<table class="table">
<thead>
<tr>
{arr.map(obj => Object.entries(obj).map(([key, value]) => (
<th scope="col">{key}</th>
)))}
</tr>
</thead>
<tbody>
{arr.map(obj => Object.entries(obj).map(([key, value]) => (
<tr>
<td>{value.activityType}</td>
<td>{value.durationInSeconds}</td>
</tr>
)))}
</tbody>
</table>
Based on your comments, it seems like maybe this is what you want:
Note to future views -- now not applicable to the changed question:
const modArray = Object.keys(arr[0]).map(i => {
return {id: i, ...arr[0][i]}
});
const rowKeys = Object.keys(modArray[0]).filter(i => i !== "id")
export default function Demo() {
return (
<div>
<table class="table">
<thead>
<tr>
{modArray.map(i => <th key={i.id}>{i.id}</th>)}
</tr>
</thead>
<tbody>
<tr>
{modArray.map(item => <td key={item.id}>{item.activityType}</td>)}
</tr>
<tr>
{modArray.map(item => <td key={item.id}>{item.durationInSeconds}</td>)}
</tr>
{/* Or, do it dynamically */}
{rowKeys.map(rowKey => <tr key={rowKey}>
{modArray.map(item => <td key={item.id}>{item[rowKey]}</td>)}
</tr>)}
</tbody>
</table>
</div>
);
}
Note that at the top of that code sample, I transformed your input into a more usable format. Your beginning arr, for example, was an array of just 1 object, which had multiple keys, which really seemed to be the array you wanted to iterate over.
Hopefully this is at least close to what you're looking for.
Update 2, based on your changed question:
export default function Demo() {
return (
<div>
<table className="table">
<thead>
<tr>
{arr[0]["Demo"].map(i => <th key={i._id}>{i._id}</th>)}
</tr>
</thead>
<tbody>
<tr>
{arr[0]["Demo"].map(item => <td key={item._id}>{item.name}</td>)}
</tr>
</tbody>
</table>
</div>
);
}
The issue is here:
{arr.map(obj =>
Object.entries(obj).map(([key, value]) => (
<table class="table">
here you have put the entire table inside the loop instead of putting a single table row. So on every iteration it generates a new table. To resolve this issue, try this:
Try this:
<table class="table">
<thead>
<tr>
<th scope="col">Heading 1</th>
<th scope="col">Heading 2</th>
</tr>
</thead>
<tbody>
{
arr.map(obj =>
Object.entries(obj).map(([key, value]) => (
<tr>
<td>{value.activityType}</td>
<td>{value.durationInSeconds}</td>
</tr>
)))}
</tbody>
</table>
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) {
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>
);
}
});