Handling array length in React - javascript

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>
)
}

Related

how do I fix my code in order to display one 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'm trying to put the data I'm receiving from my API into a table but nothing is displayed

I'm getting data from my API, when I make a console.log or JSON.stringify in the API data it shows without problems but when I pass the data in a table with the map, simply nothing is presented in the table. .
const [users, setUsers] = useState([]);
const loadUser = () => {
getUsers().then(data => {
if(data.error) {
console.log(data.error)
}else{
setUsers(data)
}
})
}
const inforUsers = () => {
return(
<Fragment>
<table className="table table-bordered mb-5">
<thead className="thead-dark">
<tr>
<th scope="col">Id</th>
<th scope="col">Nome</th>
<th scope="col">Email</th>
<th scope="col">role</th>
<th scope="col">createdAt</th>
</tr>
</thead>
<tbody scope="row">
{Object.keys(users).map((values, key) => (
<tr key={key}>
<td>
{values._id}
</td>
<td>
{values.name}
</td>
<td>
{values.email}
</td>
<td>
{values.role === 1? 'Admin' : 'Simples User'}
</td>
<td>
{values.createdAt}
</td>
</tr>
))}
</tbody>
</table>
</Fragment>
)
}
I think you are confused about the data you have in hand. The key is the id for each object, so if you want that data, you should access the users object by each of the keys/ids you get from Object.keys. A brief example:
{Object.keys(users).map(id => (
{users[id]._id}
))}

Switching between 2 table objects in React JS

I'm pretty new to react and I'm having trouble switching between these 2 tables. I looked at the documentation for inline conditional statements and this was all I got. I want it to switch between the tables after clicking the button.
const customer_table = (
<Table striped bordered hover>
<thead>
<tr>
<th>ID</th>
<th>Org Name</th>
<th>Status</th>
<th>More Info</th>
</tr>
</thead>
<tbody>
{customers.map(customer =>
<tr>
<td id="customer_id">{String(customer.customer_id).padStart(4, '0')}</td>
<td id="name">{customer.org_name}</td>
<td id="email">{customer.cus_status}</td>
<td><Link to={{
pathname: `/profile/${customer.customer_id}`,
query: { customer_id: `${customer.customer_id}`}
}} className="btn btn-primary">Profile</Link></td>
</tr>
)}
</tbody>
</Table>
)
return (
<td>
<div className="toggleCustomers">
<button onClick={toggleDeavtivated} >Show Deactivated Customers</button>
</div>
<ul>
{
show_deactivated ? `${deactivatedCustomer_table}`
: `${customer_table}`
}
</ul>
</td>
);
To do that you need to use state, you can read about this from this url
By using state you can update rendering by track any change from button, for example for your case:
const [firstTable, setFirstTable] = useState(true);
return (
<div>
<p>You clicked {firstTable}</p>
<button onClick={() => setFirstTable(!firstTable)}>
Click me
</button>
{ firstTable ? ${deactivatedCustomer_table}`
: `${customer_table}`
}
</div>
);

how to generate unique id for table cells using map

I want to set a unique id for each MenuItem, but I don't know how to do this with map() function nested in another one
<table className={classes.table}>
<thead>
<tr>
<td />
{sit.sit.map(sit => (
<td className={classes.sitCell} align="center" key={sit}>
{sit}
</td>
))}
</tr>
</thead>
<tbody>
{sit.row.map(row => (
<tr key={row}>
<td className={classes.rowCell} align="left">
{row}
</td>
{sit.sit.map(sit => (
<td className={classes.sit} key={(id = id + 1)}>
<MenuItem
id={sitId}
onClick={handleSitClick}
disabled={selected}
className={classes.sit}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
Provided code looks fine for me. You can use uuid or any other similar package to generate keys.
Your question is not fully clear i think its may be help you .
<table className={classes.table }>
<thead>
<tr>
<td></td>
{
sit.sit.map((sit) => (<td className={classes.sitCell} align='center' key={sit}>{sit}</td>))
}
</tr>
</thead>
<tbody>
{
sit.row.map((row,index )=> (
<tr key={index}>
<td className={classes.rowCell} align='left'>{row}</td>
{
sit.sit.map((sit) => (<td className={classes.sit} key={id = id+1}><MenuItem id={sitId} onClick={handleSitClick} disabled={selected} className={classes.sit}></MenuItem></td>))
}
</tr>
))
}
</tbody>
</table>
you can just add (row,index) like this
Ideally you need to use a key which is some kind of unique id from your api response. Check your api response data structure, if it has an unique id, then use it. Else use the map array index.
Like this
<table className={classes.table}>
<thead>
<tr>
<td></td>
{
sit.sit.map((sit) => (<td className={classes.sitCell} align='center' key={sit}>{sit}</td>))
}
</tr>
</thead>
<tbody>
{
sit.row.map(row => (
<tr key={row}>
<td className={classes.rowCell} align='left'>{row}</td>
{
sit.sit.map((sit, index) => (<td className={classes.sit} key={index}><MenuItem id={index} onClick={handleSitClick} disabled={selected} className={classes.sit}></MenuItem></td>))
}
</tr>
))
}
</tbody>
</table>

Conditional rendering in ReactJS between button and normal text

I have one object array say defect and now if the status of the defect is open then it should show as button and it should read close the defect and if it is closed, then instead as button it should just mention as closed.
So, here statusRender is the issue and is now working as expected in the last column. Cant figure out what I am missing. Any leads?
render() {
if (defect.defect_status == 'open') {
statusRender = <button key={index} data-id={defect.id} onClick={() => this.showAlert(defect.defect_id)}>{defect.defect_status}</button>;
} else {
statusRender = { defect.defect_status };
}
return (
<div>
<table className="table table-bordered table-hover">
<thead>
<tr>
<th>Defect ID</th>
<th>Category</th>
<th>Description</th>
<th>Priority</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{this.state.defectList.map((defect, index) => {
return (
<tr key={index}>
<td> {defect.defect_id} </td>
<td>{defect.defect_category}</td>
<td>{defect.defect_description}</td>
<td>{defect.defect_priority}</td>
<td> {statusRender}
</td>
</tr>
);
})
}
</tbody>
</table>
</div>
)
}
it is a scope issue you cannot declare defect outside of the map function
{this.state.defectList.map((defect,index) => {
return (
<tr key={index}>
<td> {defect.defect_id} </td>
<td>{defect.defect_category}</td>
<td>{ defect.defect_description}</td>
<td>{ defect.defect_priority}</td>
<td>
{
defect.defect_status === 'open'
? <button key={index} data-id={defect.id} onClick = {() => this.showAlert(defect.defect_id)}>{defect.defect_status}</button>;
: defect.defect_status;
}
</td>
</tr>
);
})
}
Thanks to user1095118 removing the semi colons did the job. I was missing the correctness of the curly braces which solved the issue
{
defect.defect_status == 'open'
?<button key={index} data-id={defect.id} onClick = {() => this.showAlert(defect.defect_id)}>{defect.defect_status}</button> : defect.defect_status
}
If you just need to access status string instead of the button maybe you should remove brackets in your if else statement
render() {
if(defect.defect_status=='open') {
statusRender = <button key={index} data-id={defect.id} onClick = {() => this.showAlert(defect.defect_id)}>{defect.defect_status}</button>;
} else {
// No brackets here ?
statusRender = defect.defect_status;
}
return (
<div>
<table className="table table-bordered table-hover">
<thead>
<tr>
<th>Defect ID</th>
<th>Category</th>
<th>Description</th>
<th>Priority</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{this.state.defectList.map((defect,index) =>{
return(
<tr key={index}>
<td> {defect.defect_id} </td>
<td>{defect.defect_category}</td>
<td>{ defect.defect_description}</td>
<td>{ defect.defect_priority}</td>
<td> {statusRender}
</td>
</tr>
);
})
}
</tbody>
</table>
</div>
)
}
}

Categories