Render Modal for Dynamic Table in React - reactjs

I have a dynamic table whose items I render through a list. The idea is that, on clicking the name of a row, a modal will open and that will print certain values retrieved from the backend server.
When I click on a specific person's name, the modal loads for all elements in the table. How do I fix this?
The rendered code for the table is as follows:-
<table className="table">
<thead>
<tr>
<th>S. NO.</th>
<th>NAME</th>
<th>ADDRESS</th>
<th>TELEPHONE</th>
<th>EMAIL</th>
<th>AGE</th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.personData.map((pRow, idx) => (
<>
<PopUp hideModal={this.hideModal} show={this.state.show} id={siteRow.id} />
<tr key={pRow.id}>
<td>{idx + 1}</td>
<td> <a onClick={this.showModal}> {pRow.name} </a> </td>
<td>{pRow.address}</td>
<td>{pRow.phone}</td>
<td>{pRow.email}</td>
<td>{pRow.age}</td>
<td>
{" "}
<DeleteButton id={pRow.id} onDelete={this.onDelete} />{" "}
</td>
</tr>
</>
</tbody>
</table>
In the code for the table, the placement of the PopUp component is such because I want to pass the ID of the particular site to the modal.
This is the rendered code for the modal:-
showModal(e) {
this.setState({
show: true
});
}
hideModal(e) {
this.setState({
show:false
});
this.props.hideModal && this.props.hideModal(e);
}
render() {
if(!this.props.show){
return null;
}
return (
<>
<div>
The ID of the person is: {this.props.id}
<button type="button" className="btn theButton" onClick={this.hideModal}>CLOSE</button>
</div>
</>
);
}
This is very rudimentary code and I haven't added much CSS so this just opens up in the table itself. I want to change this but given the placement of the PopUp component and the fact that I want to pass the ID to the component, I'm not sure how to go about it.

The problem is that you have a series of Popups, but only a variable controlling their visibility. Consider replacing the show state as a simple visibility with a showId one, meant as the "id" of the Popup to be shown.
showModal(id) {
this.setState({
showId: id
});
}
hideModal(e) {
this.setState({
showId: null
});
this.props.hideModal && this.props.hideModal(e);
}
Then:
<table className="table">
<thead>
<tr>
<th>S. NO.</th>
<th>NAME</th>
<th>ADDRESS</th>
<th>TELEPHONE</th>
<th>EMAIL</th>
<th>AGE</th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.personData.map((pRow, idx) => (
<>
<PopUp hideModal={this.hideModal} show={this.state.showId === pRow.id} id={siteRow.id} />
<tr key={pRow.id}>
<td>{idx + 1}</td>
<td> <a onClick={() => this.showModal(pRow.id)}> {pRow.name} </a> </td>
<td>{pRow.address}</td>
<td>{pRow.phone}</td>
<td>{pRow.email}</td>
<td>{pRow.age}</td>
<td>
{" "}
<DeleteButton id={pRow.id} onDelete={this.onDelete} />{" "}
</td>
</tr>
</>
</tbody>
</table>
I did not try the code, but I think it should work.

Related

React/Typescript Can't render a React.Fragment inside <tbody>:

I am trying to use a map to render <tr> elements within a <tbody>. This conditionally will render a second <tr> if there is additional content to be displayed under first <tr>. I have wrapped both in a React.Fragment and this is working fine, but when running the tests I get:
Warning: validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s%s
Table:
<table className='document-table borderless row-height-sm'>
<thead>
<tr>
{tableData.headers.map((th) => (
<th key={`document-table-th-${th.value}`}>{intl.formatMessage(th.descriptor, { value: th.value })}
<button onClick={() => { sortRows(th.value) }} className='ghost'>
<Icon data-ref="action-item-chevron" role="img" name='sort' aria-label="Sort arrows" />
</button>
</th>
))}
</tr>
</thead>
<tbody>
{sortedTableRows.map((tr, i) => (
<DocumentTableRow
key={tr.id}
rowData={tr}
rowIndex={i}
openRows={openRows}
setOpenRows={setOpenRows}
/>
))}
</tbody>
</table>
Table Row:
return (
<React.Fragment>
<tr key={`document-table-tr-${rowData.id}`}>
{rowData.values.map((td, j) => (
<td className={`document-table-td-${j} ${j === rowData.values.length - 1 ? 'flex align-items-center justify-content-space' : ''}`} key={`document-table-td-${td.sortKey.replace(' ', '-')}`}>
{intl.formatMessage(td.descriptor, {value: td.value})}
{j === rowData.values.length - 1 &&
<button onClick={() => { rowData.additionalContent && rowData.additionalContent.value ? setOpenRow(rowIndex) : null }} className='ghost'>
<Icon data-ref={`document-table-row-${rowIndex}`} role="img" name={`chevron-${openRows[rowIndex] ? 'up' : 'down'}`} aria-label="Expand row" />
</button>
}
</td>
))}
</tr>
{rowData.additionalContent && rowData.additionalContent.value && openRows[rowIndex] &&
<tr className='additional-content'><td className='pad-left-xl'>{rowData.additionalContent.value}</td></tr>
}
</React.Fragment>
);
};
Rendered html:
<table class="document-table borderless row-height-sm">
<thead>
<tr>
<th>Document type</th>
<th>Jurisdiction</th>
<th>Upload date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td class="document-table-td-0 ">Passport</td>
<td class="document-table-td-1 ">Germany</td>
<td class="document-table-td-2 ">Uploaded 2021-10-12</td>
<td class="document-table-td-3 flex align-items-center justify-content-space">Rejected</td>
</tr>
<tr class='additional content'>
<td>Rejected due to expiration</td>
</tr>
</tbody>
Why is it complaining about DOM nesting when the rendered html is valid, and why only in tests and not when rendering the component in the application?

Objects are not valid as a React child use an array instead

I'm getting "Objects are not valid as a React child" on this block:
<table className="table">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Genre</th>
<th scope="col">Stock</th>
<th scope="col">Rate</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{movies.filter(movie => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
<Like
onClick={() => this.handleLike(movie)}
liked={movie.liked}
/>
</td>
<td>
<button
onClick={() => this.handleDelete(movie._id)}
className="btn btn-danger m-2"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
The exact error message reads: Error: Objects are not valid as a React child (found: object with keys {_id, title, genre, numberInStock, dailyRentalRate, publishDate, liked}). If you meant to render a collection of children, use an array instead.
No matter what I do to troubleshoot this the error won't go away. Can anyone explain ?
This:
<tbody>
{movies.map(movie => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
<Like
onClick={() => this.handleLike(movie)}
liked={movie.liked}
/>
</td>
<td>
<button
onClick={() => this.handleDelete(movie._id)}
className="btn btn-danger m-2"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
Should look like this:
<tbody>
<MovieComponent
movie={movie}
likeOnCLick={this.handleLike(movie)}
/>
</tbody>
You're not filtering anything, so just use map(), and pass the results into a React.Component that you define elsewhere.
The filter method is used to select certain items of an array. It seems that you have confused this with the map method, which should work in this scenario.
filter: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Having an issue with rendering table in a React component

I am trying to render a table inside a react component but having trouble aligning the rows with the header
render() {
console.log("Top Searches",this.props.topSearches)
return(
<div className="topsearches">
<table border="2" align="center">
<th> Search Term </th>
<th>Count </th>
{this.props.topSearches.map((top_search, index) => (
<tr>
{Object.keys(top_search).map(function(key) {
return <div>
<tbody>
<td align="center">{key} </td>
<td align="right">{top_search[key]}</td>
</tbody>
</div>
})}
</tr>
))}
</table>
</div>
)
}
This is the output i am getting
I assume, you are trying to achieve something like this.
I have edited this in codesandbox here.
https://codesandbox.io/s/sparkling-sound-yd1ih
return (
<div className="topsearches">
<table border="2" align="center">
<thead>
<th> Search Term </th>
<th>Count </th>
</thead>
<tbody>
{props.topSearches.map((top_search, index) =>
Object.keys(top_search).map((key, index) => {
return (
<tr>
<td>{key}</td>
<td>{top_search[key]}</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
);
Modification I made to achieve this:
Added <thead>
Corrected <tbody> loop
Please let me know if you have any further questions!

Finding the sum of column values from a table and displaying them

Say I have a table that stores items and their weights and I want to sum the total weight from that column and display that at the top on the table. Not using anything other than react/jsx. For more clarification, my table is pulling data stored on a node.js/express server.
I tried writing a function to go into my table component that used reduce, it did not work at all and I'm not sure how to go about it at this point.
Would like the total weight to be displayed next to the table header.
const GearTable = (props) => {
return(
<div style={styles.box}>
<h3>Your Gear Locker</h3>
<br />
<br />
<Table hover style={[styles.font, styles.box]}>
<thead>
<tr>
<th>ID</th>
<th>Item Name</th>
<th>Description</th>
<th>Weight</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{props.gear.map((gear, id) => {
return(
<tr key={id}>
<th scope="row" style={styles.font}> {gear.id}</th>
<td style={styles.font}> {gear.itemName}</td>
<td style={styles.font}> {gear.description}</td>
<td style={styles.font}> {gear.weight.value}</td>
<td style={styles.font}> {gear.quantity}</td>
<td>
<Button className="btn btn-secondary" size="lg" style={styles.font} id={gear.id} onClick={e => props.update(e, gear)}>Update Locker</Button>
<Button className="btn btn-secondary" size="lg" style={styles.font} id={gear.id} onClick={props.delete}>Delete Item</Button>
</td>
</tr>
)
})
}
</tbody>
</Table>
</div>
);
}
const GearTable = (props) => {
return(
Your Gear Locker
ID
Item Name
Description
Weight
Quantity
{props.gear.map((gear, id) => {
return(
{gear.id}
{gear.itemName}
{gear.description}
{gear.weight.value}
{gear.quantity}
props.update(e, gear)}>Update Locker
Delete Item
)
})
}
);
}

want to show data in new page or something like this where my table disappair

hi i have a table with data i want when i clicked on any row its shows it id name and date of birth in blank page or any where ,,where my table disappair..here is code of my table
<table className='content'>
<tbody>
<th>ID</th>
<th>Name</th>
<th>Birth</th>
{
this.state.data.map(item=>{
return (
<tr onClick={this.handleclick}>
<td >{item.id}</td>
<td >{item.name}</td>
<td >{item.dateofBirth}</td>
</tr>
);
})
}
</tbody>
</table>
and here i want to write onclick method
handleclick(event,id,name,dateofBirth)
{
event.preventDefault();
console.log('click fun
active',this.state.data.id,this.state.name,this.state.dateofBirth);
}
You're not passing anything into your handleClick method, apart from event, which it's passed automatically. If you want to pass all the other variables in, then you need to wrap the call in a closure function, like so:
<table className='content'>
<tbody>
<th>ID</th>
<th>Name</th>
<th>Birth</th>
{
this.state.data.map(item=>{
return (
<tr onClick={e => this.handleclick(e, item.id, item.name, item.dateofBirth)}
key={item.id}>
<td >{item.id}</td>
<td >{item.name}</td>
<td >{item.dateofBirth}</td>
</tr>
);
})
}
</tbody>
</table>
You should also add a key to your tr.

Resources