Hi I use react js and I'm new. I want to use Table pagination material-ui for a table that does not belong to material-ui.For example, I want 2 row per page.I use the reactStrap table. If possible, tell me how to do it.
render() {
const items = this.props.items.map(item => {
return (
<tr key={item.id}>
<td>
<div style={{ width: "110px" }}>
<Button
color="success"
buttonLabel="Edit"
item={item}
updateState={this.props.updateState}
>
Edit
</Button>
<Button color="danger" onClick={() => this.deleteItem(item.id)}>
Del
</Button>
</div>
</td>
<th scope="row">{item.id}</th>
<td>{item.name}</td>
<td>{item.username}</td>
<td>{item.email}</td>
</tr>
);
});
return (
<div
style={{
maxHeight: "600px",
overflowY: "auto"
}}
>
<Table responsive hover>
<thead>
<tr>
<th>Action</th>
<th>ID</th>
<th>name</th>
<th>username</th>
<th>email</th>
</tr>
</thead>
<tbody>{items}</tbody>
</Table>
</div>
);
}
}
You can see my table in CodeSandbox.Thanks in advance for your answers
I prepared an example Only for pagination. You need to work on api calling with the paging parameters. As your api is returning only 10 records, so I set rowsPerPage: 5 so that you can get the reflection of changing page. but default value should be rowsPerPage: 10
state = {
items: [],
page: 0,
rowsPerPage: 5
};
Update:
You need update the url with the state value like below:
let url = `https://reqres.in/api/users?page=${this.state.page +
1}&per_page=${this.state.rowsPerPage}`;
And then call api when you change the page. Please check the below link for code.
Here is the Code Sandbox
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Pagination from '#material-ui/lab/Pagination';
const useStyles = makeStyles((theme) => ({
root: {
"& > * + *": {
marginTop: theme.spacing(2),
},
},
}));
export default function PaginationComponent(props) {
const classes = useStyles();
const [page, setPage] = React.useState(1);
const handleChange = (value) => {
setPage(value);
props.paginationHandler(value);
};
return (
<div className={classes.root}>
<Pagination
count={props.totalCount}
page={page}
variant="outlined"
color="primary"
onChange={handleChange}
/>
</div>
);
}
Related
Inactive State:
Active State:
When I toggle a single button the state of every other button changes which I don't want. Is there any way to control the state of these toggles individually
This is my code for columns to be used in the table:
const COLUMNS = useMemo (() => [
{
Header: "Username",
accessor: "Username",
textAlign: "left",
sortable: false,
},
{
Header: "Status",
accessor: "Status",
textAlign: "center",
Cell: ({ value, cell: { row } }) => (
<div>
<FormControlLabel
control={
<IOSSwitch
checked={status}
onClick={toggler}
name="status"
/>
}
/>
{status ? <span>Active</span> : <span>Inactive</span>}
</div>
),
},
This is my code for the table:
<Table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<Th
{...column.getHeaderProps({
style: { textAlign: column.textAlign },
})}
>
{column.render("Header")}
</Th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row) => {
prepareRow(row);
return (
<Tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<Td
{...cell.getCellProps({
style: { textAlign: cell.column.textAlign },
})}
>
{cell.render("Cell")}
</Td>
);
})}
</Tr>
);
})}
</tbody>
</Table>
You have to assign each button to its individual state for example you can have three states for three buttons.
your states should look like this:
const [firstButton, setFirstButton] = useState(false)
const [secondButton, setSecondButton] = useState(false)
const [thirdButton, setThirdButton] = useState(false)
and in your onClicks you should say:
for example for second button:
onClick={()=>setSecondButton(false)}
and when you want to use this state in your table you should match every button with its state
ps: if your number of buttons are not fix, you should write a function to return an object which keys are button names and values are false ( false is default and this should change when clicks ).
Because of the immutability of states in react.js, you should clone the state and change what you want and leave every other thing as it is then update the whole state.
and use it like:
onClick={()=>setButtonsState({...buttonsState, secondButton:false})}
where buttonsState is like:
const [buttonsState, setButtonsState] = useState(
{
firstButton: false,
secondButton: false,
thirdButton: false
}
)
I've been trying for a while now, with no luck on trying to get it to where a user clicks on a table item that it brings up a window with the information on what they clicked. I have a JSON file and would like it to read the information from the file instead of the actual file page.
Here is the main app page
import './App.scss';
import list from './list.json';
import Table from 'react-bootstrap/Table';
import MyVerticallyCenteredModal from './components/modal';
function App() {
const [modalShow, setModalShow] = useState(false);
const [menu, setMenu] = useState(
{
food: ""
}
);
const handleChange = (event) => {
setMenu({
food: event.target.value
});
}
const [looped] = useState(
list.map((obj, i) => {
return (
<tr key={i} onClick={() => {
setModalShow(true);
handleChange(obj.food)
console.log(obj.food)
}
}>
<td>{i + 1}</td>
<td>{obj.food}</td>
<td>{obj.description}</td>
<td>${obj.price.toString()}</td>
</tr>
)
})
);
// console.log(menu);
return (
<div className="menu-template container-lg">
<div className="mt-5 mb-5 d-flex">
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Meal Name</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{
looped
}
</tbody>
</Table>
</div>
<MyVerticallyCenteredModal
food={food}
description={description}
price={price}
show={modalShow}
onHide={() => setModalShow(false)}
/>
</div>
);
}
export default App;
Here is the modal
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import image from '../assets/image1.jpg';
function MyVerticallyCenteredModal(props) {
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header>
<Modal.Title id="contained-modal-title-vcenter">
<img src={image} alt="..." width="100%" />
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>
Some Tasty Food!</h4>
<p>
This is the best food you've ever eaten!
</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={props.onHide} variant="danger">Close</Button>
</Modal.Footer>
</Modal>
);
}
export default MyVerticallyCenteredModal;```
From the first look at this, it doesn't look like you are adding an event to the handleChange() function, just passing a parameter from the obj dictionary. Also, since you are only showing a modal when there is a value in the menu, I would just use that as the indicator to show.
Here's a small sample:
const [menu, setMenu] = useState(null);
const [looped] = useState(
list.map((obj, i) => {
return (
<tr key={i} onClick={() => setMenu({food: obj.food, description: obj.description, price: obj.price, <etc...>})}>
<td>{i + 1}</td>
<td>{obj.food}</td>
<td>{obj.description}</td>
<td>${obj.price.toString()}</td>
</tr>
)
})
);
// console.log(menu);
return (
<div className="menu-template container-lg">
<div className="mt-5 mb-5 d-flex">
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Meal Name</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{
looped
}
</tbody>
</Table>
</div>
<MyVerticallyCenteredModal
{...menu} /** Just spreading out the dictionary from the state */
show={!!menu} // Double ! makes it a true or false
onHide={() => setMenu(null)} // or false..
/>
</div>
);
} ```
----------
Because you have now passed the props from that state, you can now use
them on the modal like this:
function MyVerticallyCenteredModal(props) {
const {food, descriptions, price} = props
console.log('view passed props')
....
}
In the model file.
When you're exporting the component remove default.
Use something like this-
export {MyVetrical....}
Lemme know if it works
I'm having an issue where react-table (version 7.1.0) seems to be reinitializing any time the page needs to be re-rendered. Using the code below (running example here) as an example, if you were to change the pageIndex value (by switching to a different page), then hit Dummy Button, you can observe that the pageIndex resets back to its default value of 0. The same thing happens if you modify the pageSize in that it automatically resets back to its default value of 10 any time the page has to be re-rendered.
import React, { useState } from "react";
import makeData from "./makeData";
import { useTable, usePagination } from "react-table";
import { ButtonToolbar, Button, Table } from "react-bootstrap";
// Nonsense function to force page to be rendered
function useForceUpdate() {
const [value, setValue] = useState(0);
return () => setValue(value => ++value);
}
export default function App() {
const forceUpdate = useForceUpdate();
const columns = React.useMemo(
() => [
{
Header: "Name",
columns: [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
accessor: "lastName"
}
]
},
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age"
},
{
Header: "Visits",
accessor: "visits"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Profile Progress",
accessor: "progress"
}
]
}
],
[]
);
const data = React.useMemo(() => makeData(100000), []);
let ArchiveTable = ({ columns, data }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize }
} = useTable(
{
columns,
data
},
usePagination
);
return (
<div style={{ textAlign: "center" }}>
<Table striped bordered {...getTableProps()} className="datasets">
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr
{...row.getRowProps()}
className={row.isSelected ? "selected" : row.className}
>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
<div className="pagination" style={{ display: "inline-block" }}>
<ButtonToolbar>
<Button
variant="light"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
size="small"
>
<span><<</span>
</Button>
<Button
variant="light"
onClick={previousPage}
disabled={!canPreviousPage}
size="small"
>
<span><</span>
</Button>
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[5, 10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<Button
variant="light"
onClick={nextPage}
disabled={!canNextPage}
size="small"
>
<span>></span>
</Button>
<Button
variant="light"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
size="small"
>
<span>>></span>
</Button>
</ButtonToolbar>
<span>
Page <strong>{pageOptions.length === 0 ? 0 : pageIndex + 1}</strong>{" "}
of <strong>{pageOptions.length}</strong>
</span>
</div>
</div>
);
};
return (
<div className="App">
<ArchiveTable data={data} columns={columns} />
<button onClick={forceUpdate}>Dummy Button</button>
</div>
);
}
I'm at a complete loss for what to do to fix this. What is the proper way to set everything up so that I don't reinitialize react-table every time the page has to be re-rendered? Said another way, if I hit the Dummy Button, I don't want the table to reset back to page 1 with a page size of 10.
Ended up figuring out the answer. The problem was I needed to move the initialization of the table outside of the rendering logic (quite obvious, in hindsight). Basically, I simply created an ArchiveTable function. For anybody who stumbles across this, you can check here for a working example.
function ArchiveTable({ columns, data }) {
// Add all the initialization and table rendering code here
// (everything that was originally part of the ArchiveTable initialization)
}
I'm working on a web-application using the MERN stack that displays a table of clients with their name, email, and phone number. I haven't implemented Redux quite yet, but I'm using 'uuid' to supplement data in the table until I can get the redux store set up. So far I have displaying the the list and adding a client to the list working fine, but I am having trouble with the pesky delete button.
This is the current ClientTable component
import React, { Component } from "react";
import { Table, Container, Button } from "reactstrap";
import { connect } from "react-redux";
import {
getClients,
addClient,
editClient,
deleteClient,
} from "../actions/clientActions";
import PropTypes from "prop-types";
const renderClient = (clients, index, id) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}
ClientTable.propTypes = {
getClients: PropTypes.func.isRequired,
client: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
client: state.client,
});
export default connect(mapStateToProps, {
getClients,
deleteClient,
addClient,
})(ClientTable);
This is the bit of code that is causing me issues
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
When I click the "delete" button I keep getting TypeError: Cannot read property 'setState' of unedefined
I know the error is because of 'this' isn't bound to anything, but I'm uncertain how to bind it within an onClick event if that is even possible or what even to bind it to. I am just lost as to how to approach this problem. (I'm still quite new to React).
If anyone has any ideas it would be greatly appreciated!
move renderClient function to ClientTable, and use it as a method of this class.
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
renderClient = (clients, index) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => this.onDeleteClient(clients.id)}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(this.renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}
I want to find the id of the row clicked in table.For instance I search for a book with the title 'Mastery' and there are 2 books with the same title but different authors.These books get shown in the table correctly. What I want to do is when I click on a particular book in the table it should open up a modal with the book details in input boxes, however when I click on any of the books the modal pops up with just the details of one of the books.
When I type in the search term ('Mastery') I get two suggestions which is the expected behaviour.
When I click on the suggested search term('Mastery') and hit enter or search button. All the books with that title('Mastery') gets populated in the Table. Also the expected behaviour.
Now when I click on the first instance of a book with title 'Mastery' this is what I get in my modal.
When I click on the second instance. I get this.
You realise that it is the same book that gets shown in the modal.
Expected Behaviour:
I want to be able to click on a book in the table and the book in that row get shown in the modal.
import React, { Component } from 'react';
import './Update.css';
// import Pace from 'react-pace-progress';
//CHILD COMPONENTS
import Search from '../../Search/Search';
import Modal from './Modal/Modal';
const Table = ({ data, openBookDetails }) => (
<table className="table table-hover">
<thead>
<tr className="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow key={row._id} row={row} openBookDetails={openBookDetails}/>
)}
{/* Remove key={row.id} inside TableRow because it is not used to set the data in the table */}
</tbody>
</table>
)
const TableRow = ({ row, openBookDetails }) => (
<tr className="table-light" onClick={openBookDetails}>
<th scope="row" >{row.title}</th>
<td >{row.author}</td>
<td >{row.isbn}</td>
<td >24</td>
</tr>
)
class Update extends Component{
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: [],
isBookDetailsOpen: false,
searchForEmpty: true,
isDataFetching: true,
title: '',
author: '',
isbn: ''
};
}
setTableData = (searchedBook) => {
this.setState({searchedBooks: searchedBook});
}
openBookDetails = () => {
this.setState({ isBookDetailsOpen: true});
console.log(this.state.searchedBooks);
this.setState({ title: this.state.searchedBooks.title});
this.setState({ author: this.state.searchedBooks.author});
this.setState({ isbn: this.state.searchedBooks.isbn});
}
closeBookDetails = () => {
this.setState({ isBookDetailsOpen: false});
}
changeIsSearchForEmpty = () => {
this.setState({ searchForEmpty: !this.state.searchForEmpty });
}
changeIsDataFetching = () => {
this.setState({isDataFetching: !this.state.isDataFetching})
}
render(){
const showHideAlert = this.state.searchForEmpty ? 'alert alert-danger d-none' : 'alert alert-danger d-block';
// const showHideProgress1 = this.state.isDataFetching ? 'progress' : 'progress display-none';
const showHideProgress = this.state.isDataFetching ? 'progress progress-bar progress-bar-striped bg-success progress-bar-animated d-block' : 'progress-bar progress-bar-striped progress-bar-animated d-none';
const style= {
width: "100%",
height: "8px"
}
return(
<div>
{/* Uninstall react-pace-progress if not going to be used */}
{/* {this.state.isDataFetching ? <Pace color="#27ae60" height="0.5px"/> : null} */}
<div style={style}>
<div class={showHideProgress} role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style={style}></div>
</div>
<div className={showHideAlert}>
<strong>Sorry!</strong> You have to type in a search word/phrase.
</div>
<div className='px-3 pt-3'>
<Search
state={this.state}
setTableData={this.setTableData}
changeIsSearchForEmpty={this.changeIsSearchForEmpty}
changeIsDataFetching={this.changeIsDataFetching} />
<Table
data={this.state.searchedBooks}
openBookDetails={this.openBookDetails} />
<Modal
data={this.state.searchedBooks}
isBookDetailsOpen={this.state.isBookDetailsOpen}
closeBookDetails={this.closeBookDetails}
updateBookDetails={this.updateBookDetails}
grabTitle={this.grabTitle}
grabAuthor={this.grabAuthor}
grabISBN={this.grabISBN}
state={this.state} />
</div>
</div>
);
}
}
export default Update;
You're going to need some kind of ID or index on the tr element o your TableRow component. You can accomplish your goal without adding any extra react elements to your code, but your onClick function callback must be able to get the actual value.
If you take a look at the code below:
import React from "react";
import ReactDOM from "react-dom";
const data = [
{ id: "one", firstname: "john", lastname: "smith" },
{ id: "foo", firstname: "peter", lastname: "parker" }
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clicked_id: null
};
}
onClick = event => {
const id = event.currentTarget.getAttribute("data-rowid");
console.log(id);
this.setState({ clicked_id: id });
};
render() {
return (
<div>
<div>Clicked ID: {this.state.clicked_id}</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
{data.map(e => (
<tr key={e.id} data-rowid={e.id} onClick={this.onClick}>
<td>{e.id}</td>
<td>{e.firstname}</td>
<td>{e.lastname}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
you can see that the tr actually has a data-rowid element that is later used by the onClick method to extract the value. You can use other tags, I just chose that one for myself.
Edit to add:
If you want to take a look at the code above working, check out this codesandbox link:
https://codesandbox.io/s/4368l97lqx
Second edit:
You could just refactor your TableRow component to call the openBookDetails prop function with the parameter that you want:
class TableRow extends React.Component {
handleClick = (event) => {
event.stopPropagation();
const { row, openBookDetails } = this.props;
openBookDetails(row._id);
};
render() {
const { row } = this.props;
return (
<tr className="table-light" onClick={this.handleClick}>
<th scope="row">{row.title}</th>
<td>{row.author}</td>
<td>{row.isbn}</td>
<td>24</td>
</tr>
);
}
}
As per your code highlighted below:
<tbody>
{data.map(row =>
<TableRow key={row._id} row={row} openBookDetails={openBookDetails}/>
)}
{/* Remove key={row.id} inside TableRow because it is not used to set the data in the table */}
</tbody>
I think you need to pass the row values to openBookDetails like:
<TableRow key={row._id} row={row} openBookDetails={openBookDetails(row)}/>
and accordingly use the row values sent inside the openBookDetails function
Just wrote a similar code like this
`
<TableBody>
{rows.map((row, index) => (
<TableRow key={row.id} style={{
backgroundColor: deletingId == row.id ? 'salmon' : '',
color: deletingId == row.id ? 'white' : '',
}}>
<TableCell component="th" scope="row">
{index + 1}
</TableCell>
<TableCell align="right">{row.stockName}</TableCell>
<TableCell align="right">{row.stockCount}</TableCell>
<TableCell
align="right">{row.categoryResponseDTOS.length > 0 ? row.categoryResponseDTOS.map(category => {
return category.categoryName + ", "
}) : "---"} </TableCell>
<TableCell>
<ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button color="warning" id={row.id}><EditIcon/> EDIT</Button>
<Button color="error" id={row.id} onClick={(e) => handleDelete(e)}>
<DeleteIcon/> DELETE</Button>
<Button color="success"> <ZoomInSharpIcon/> DETAIL</Button>
</ButtonGroup>
</TableCell>
</TableRow>
))}
</TableBody>
`
if you have a key you check the state you created if equals to it