This may already have an answer but I have not been able to find one that doesn't suggest a work around that doesn't make use of ref. That said, using ref may not be the preferred approach.
I have a table where each row has a popover menu as the last column. Currently, the ref will always point to the last mapped item (last row's menu).
How can I make sure each row's menu has its own reference?
const [showMenu, setShowMenu] = useState(false);
const menu = useRef(null);
return (
<table>
<caption>Table</caption>
<thead>
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
<th>Col 4</th>
</tr>
</thead>
<tbody>
{data.map((item, index) =>
<tr key={ index }>
<td>{ item.a }</td>
<td>{ item.b}</td>
<td>{ item.c }</td>
<td>
<div className="popover-group">
<button
className="popover-menu-button"
onClick={ () => setShowMenu(!showMenu) }
>Show Menu</button>
<div className="popover-menu" ref={ menu }>
/* menu content */
</div>
</div>
</td>
</tr>
)}
</tbody>
</table>
)
I should add that I will need to be able to access the specific menu elsewhere like here
useEffect(() => {
if (showMenu){
menu.current.style.setProperty('opacity', '1')
menu.current.style.setProperty('display', 'block')
} else {
menu.current.style.setProperty('opacity', '0')
menu.current.style.setProperty('display', 'none')
}
}, [showMenu])
So I will need to access menu.current[/*some index*/]. How could I accomplish this?
Related
I am not a react expert, and I am trying to integrate a gridjs component with react-beautiful-dnd library. The objective is to add drag and drop functionaly to a gridjs table.
I have create a react component, that looks like this:
import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
function ForwardGrid({userdata}, ref) {
const users = userdata.data;
return (<>
<div ref={ref}>
<DragDropContext>
<table>
<thead>
<tr>
<th>#</th>
<th>Username</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<Droppable droppableId="droppable-1">
{(provider) => (
<tbody
className="text-capitalize"
ref={provider.innerRef}
{...provider.droppableProps}
>
{users?.map((user, index) => (
<Draggable
key={user.name}
draggableId={user.name}
index={index}
>
{(provider) => (
<tr {...provider.draggableProps} ref={provider.innerRef}>
<td {...provider.dragHandleProps}> =</td>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{user.gender}</td>
</tr>
)}
</Draggable>
))}
{/*{provider.placeholder}*/}
</tbody>
)}
</Droppable>
</table>
</DragDropContext>
</div>
</>)
}
const forwardTable = React.forwardRef(ForwardGrid);
export default forwardTable;
this component return a forwardref, which is used in a parent component.
const tableRef = useRef(null);
const wrapperRef = useRef(null);
const [users, setUsers] = useState(userdata.data);
useEffect(() => {
console.log(tableRef.current)
const grid = new Grid({
from: tableRef.current,
}).render(wrapperRef.current);
}, []);
return (
<>
<div className="App mt-4">
<ForwardGrid ref={tableRef} userdata={userdata}/>
<div ref={wrapperRef}/>
</div>
</>
);
However, once .render(wrapperRef.current) is fired, the table is rendered but the drag and drop doesn't work. I have inspected the html generated and I have noticed that it is not correct. It does not contain all the attributes that react-beautiful-dnd would generate normally (for instance data-rbd-draggable-id, data-rbd-draggable-context-id etc).The drag and drop functionalty works fine with a normal html table.
I would expect this html generated.
<tr data-rbd-draggable-context-id="0" data-rbd-draggable-id="Subham" style="">
<td tabindex="0" role="button" aria-describedby="rbd-hidden-text-0-hidden-text-0"
data-rbd-drag-handle-draggable-id="Subham" data-rbd-drag-handle-context-id="0" draggable="false"> =
</td>
<td>Subham</td>
<td>21</td>
<td>male</td>
</tr>
insted it returns this.
<tr class="gridjs-tr">
<td data-column-id="#" class="gridjs-td">=</td>
<td data-column-id="username" class="gridjs-td">Jeevan</td>
<td data-column-id="age" class="gridjs-td">21</td>
<td data-column-id="gender" class="gridjs-td">male</td>
</tr>
any idea how can I solve the problem?
I have passed an array of objects in useState hook as the initial state and i want to iterate over it using map method in other component, i have passed the current state as prop in the other object and have used map method over it but console message still shows values.map is not a function, i know we can only use map method on array i have done that only still the error shows.
created state and passed an array of objects-
const [values, setValues] = useState([
{
name: "Vansh",
age: 22,
email: "vansh#gmail.com",
},
]);
Passed values as props in Home component-
<Home
addFormData={addFormData}
setAddFormData={setAddFormData}
values={values}
setValues={setValues}
/>
Code of my home component-
const Home = ({ values }) => { return (
<div>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
</tr>
</thead>
<tbody>
{values.map((val, index) => (
<tr>
<td key={val.index}>{val.name}</td>
<td key={val.index}>{val.age}</td>
<td key={val.index}>{val.email}</td>
</tr>
))}
</tbody>
</table>
<Link to="/form">
<button>FORM</button>
</Link>
</div> ); };
Please see the code it's working fine export below your missing export.
const Home = ({ values }) => {
// console.log(values);
return (
<div>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
</tr>
</thead>
<tbody>
{values.map((val, index) => (
<tr>
<td key={val.index}>{val.name}</td>
<td key={val.index}>{val.age}</td>
<td key={val.index}>{val.email}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Home;
Please see the code SandBoxLink
I have a question about the component panel in react
Currently, I have a requirement to create a common component for the Order and Product tables. But the two tables have different numbers of columns, one side has many columns and one side has few columns, besides there are also different table names.
I have a piece of code like this
import React from 'react';
import {Table, Image} from 'react-bootstrap';
import '../Table/index.css';
import Button from '../Button/index';
const TableItem = ({productList}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
<th>No. </th>
<th>Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{productList.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
export default TableItem;
That code I have created for the product table, but the order table is not because I do not know how to do it properly. I named fixed for the column, I know this is wrong because the order table will also retrieve components from this table, so I can not make the name like that
The order table also has the following columns: Username, address, quantity, status .....
How can I change the code in this component that can be used for both tables
Can anyone help me to explain for this, thank you so much
So you can do something like this, create a table component and pass the columns list and data in there as props that way you can control the table from the parents component and can be used in any way as you want.
const TableItem = ({data, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{
columns.map((column, index) => {
<th key={ index }>{ column.name }</th>
}
}
</tr>
</thead>
<tbody>
{data.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
and in the parent component you can do something like
const parentComponent = () => {
return {
<TableItem columns={ productColumns } data={ productData } />
<TableItem columns={ orderColumns } data={ orderData }
}
}
Note Code not tested and is pseudo
You can pass columns (Array) as a props and render it.
const columns = ['username', 'quantity', 'etc'];
const TableItem = ({List, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
If you have some common columns then you can pass one more props commonColumns like that.
const TableItem = ({List, columns, commonColumns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{commonColumns.map((name, index) => (
<th key={index}>{name}</th>
))}
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
Hope this will help you.
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.
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.