ReactJs - MaterialTable pagination, row per page not working - javascript

I'm currently using Material-table . It displays data normally however, Pagination and Row per Page dropdown is not working. Nothing happens upon clicking, next button and selected number of rows.
See below codes:
import MaterialTable from 'material-table'
const tableIcons = {
/*table icons*/
}
function Test(){
const [data, setData] = useState([]);
const getDatas = async() => {
await axios.get('/API')
.then(response => {
setData(response.data)
}
}
const columns = [
{.....} //columns
]
return(
<div>
<MaterialTable
icons = {tableIcons}
columns = {columns}
data = {data}
title = 'List of data'
actions = {[{
//add button properties
}]}
>
</MaterialTable>
</div>
)
}
export default Test;
I'm getting the following error on console upon onload and clicking pagination buttons.
On load:
On click of next button
Please help me with this. Thank you in advance.

First of all, keep in mind that the original project was discontinued, and the new direction can be found in this repository (it's a fork of the original). There will be a lot of refactorings and breaking changes, so you might want to check them out first.
Now, on your question,
since you are working with remote data you could check out the official example on how to handle this kind of data.
If your requirements don't allow you to do this, you will need to do all the handling by yourself. That means you should provide your own implementation of the Pagination component, in which you define your own behavior of onChangePage and other callbacks.
The customisation will look something like:
Pagination: (properties: any) => {
return (
<TablePagination
{...properties}
count={currentPage.total}
onChangePage={(event: any, page: number) => {
onChangePage(page);
}}
page={currentPage.startIndex / pageSize}
/>
);
}
where total, startIndex etc. will be provided by the API you consume, along with the actual data that you show in the table.
These components overrides should be provided under the components property of the material table.

Related

Having trouble paginating search results using useState, Material-UI and custom hook

I am having an issue with pagination on a page in my React application. On the page, search results are rendered when one types into the search bar (naturally). I think my issue arises from how pagination is set up on this page.
Pagination works fine as long as the user clicks back to the first page before searching for anything else. For example, if the user is on page 3 and then types something new into the search bar, the new search will not display without the user clicking 'page 1' again on the pagination bar. However if they returned to page 1 of their initial search before doing the new search, page 1 of the new search displays properly. Hopefully this makes sense. Here is the page where the issue occurs:
import React, { useState } from "react";
import Pagination from "#material-ui/core/Pagination";
import usePagination from "./usePagination.js";
export default function Main({reviews, web3}) {
const [search, setSearch] = useState("");
const [page, setPage] = useState(1);
const updateSearch = (event) => {
setSearch(event.target.value.substr(0, 20));
}
let filteredReviews = reviews.filter(
(review) => {
return review.restaurantName.indexOf(web3.utils.toHex(search)) !== -1;
});
let paginatedReviews = usePagination(filteredReviews, 2);
const handleChange = (e, p) => {
setPage(p);
paginatedReviews.jumpPage(p);
}
return (
<div className="container-fluid mt-5" style={{ minHeight: "100vh" }}>
<div className="row">
<main role="main" className="col-lg-12 ml-auto mr-auto" style={{ maxWidth: '500px' }}>
<div className="content mr-auto ml-auto">
<input type="text" className="form-control" value={search} onChange={updateSearch} />
{filteredReviews.length > 0 ? paginatedReviews.pageData().map((review, key) => {
return (
<>
<div key={key}>
// search result item
</div>
</>
)
})
{filteredReviews.length > 1
? <Pagination
count={paginatedReviews.maxPage}
page={page}
onChange={handleChange}
/>
: null
)
</div>
</main>
</div>
</div>
);
}
and here is usePagination:
import { useState } from "react";
export default function usePagination(allReviews, perPage) {
const [currentPage, setCurrentPage] = useState(1);
const maxPage = Math.ceil(allReviews.length / perPage);
function pageData() {
const start = (currentPage - 1) * perPage;
const end = start + perPage
return allReviews.slice(start, end);
}
function jumpPage(page) {
const pageNumber = Math.max(1, page);
setCurrentPage((currentPage) => Math.min(pageNumber, maxPage));
}
return { jumpPage, pageData, currentPage, maxPage }
}
I thought I could resolve the issue I'm having by adding setPage(1) to updateSearch in order to have the page automatically move to page 1 for each new search, but that didn't work, as you still had to click page 1 on the actual pagination bar for the results to show up.
Edit: I tried renaming currentPage and setCurrentPage in the hook so that they shared the same names as on my page, but that also did not work.
Thanks in advance for any help you can offer. If you need me to elaborate on anything I will happily do so.
How about updating the page in a useEffect? That way you'll make sure all hooks have run and their return values are up-to-date (useEffect runs after render). If you reset the page too early, at the same time as the search query, jumpPage might rely on stale data: your search results and the internal usePagination values like maxPage will not have had a chance to recalculate yet.
Here is a working example based off your codesandbox: https://codesandbox.io/s/restless-dust-28351
Note that to make sure useEffect runs on search change only, you need to wrap jumpPage in a useCallback so that the jumpPage function reference remains the same. In general, I'd recommend you do that to methods returned from custom hooks. This way those methods are safer to consume anywhere, including as dependencies to useEffect, useCallback etc.
Also I'd recommend destructuring the custom hook return values, so that each of them can be used on its own as a hook dependency, like jumpPage in my example above.
I've also removed the page state from App, as it's already tracked in usePagination and returned from there. Having usePagination as a single source of truth that encapsulates all your pagination stuff makes things simpler. Simplicity is a great ideal to strive for:)
Lastly, a small side note: it's best not use <br /> purely as a spacer. It clutters up the markup without contributing any useful semantics, and it's better to leave the spacing concern to CSS.
And good luck with your React endeavors, you're doing great!

Unable to set an array with useState hook

I am trying to set an array to a state hook. Basically I want to keep a track of a per-row (of grid sort of) Edit Dialog Open State. Basically per row, I have a Edit button, launches a . As all seems rendered initially, I am trying to manage the show hide by keeping an array in the parent grid component. When user clicks on the Edit button, per row, I want to pass the rowData as props.data and want to provide the Edit functionality.
To keep the state of the editDialogs (show/hide), I am making a array of objects useState hook as follows:
const [editDialogsModalState, setEditDialogsModalState] = useState([{}]); // every edit dialog has it's own state
...
function initializeEditDialogsModalState(dataSet) {
let newState = [];
dataSet.map((item) => newState.push({ id: item.id, state: false }));
return setEditDialogsModalState(newState); // **PROBLEM->Not setting**
}
function addUDButtons(currentRowDataMovie) { // my edit/delete button UI code
const currRowDataId = currentRowDataMovie.id;
return (
<span>
<button
type="button"
className="btn btn-info"
onClick={() => setEditDialogsState(currRowDataId)}
>
Edit
</button>
{editDialogsModalState[currRowDataId].state && ( // **PROBLEM->null data even after set call**
<EditMovieComponent
open={editDialogsModalState[currRowDataId].state}
onToggle={toggleEditDialogsModalState(currentRowDataMovie)}
movie={currentRowDataMovie}
/>
)}
}
......
function buildGrid() {
{
if (!ready) {
// data is not there, why to build the grid
return;
}
initializeEditDialogsModalState(movies);
...........
}
However not able to get the editStates. A screen shot from debugger where I can see the movies (REST output), ready, but not the editDialogsModalState state array.
In general, is there a better ways of implementing such per-row basis functionality where on click of a button I want to open a React-bootstrap and pass the row-specific dataitem for doing operations ? (I am learning React, so may not not yet fully aware of all pointers).
Thanks,
Pradip

Clear date picker component after pressing clear button in antd

We are using antd for datepicker and moments as util. I'm stuck for a week in this ISSUE.The thing is, in the filter sidepanel,on pressing clear,all the fields should clear or set to their default values(in case of dropdown).But the date picket is not resetting.
The above picture is the Activity component and left side to its his the filter.A basic filter with API change from backend on every action event.
useEffect(()=>{
if(clearFilter){
form.resetFields()
setActivitySearchText('')
setFromDate('')
setToDate('')
setStatusSearchText('')
onStatusChange('')
setClearFilter(false)
}
},[clearFilter])
const onChangeFromDate = dateString => {
setFromDate(new Date(dateString).toISOString())
}
const onPageToDate = dateString => {
setToDate(new Date(dateString).toISOString())
}
<StyledDatePicker
allowClear={false}
format={dateFormat}
disabledDate={disabledFromDate}
placeholder={'From'}
onChange={(fromdate, dateString) =>
onChangeFromDate(fromdate, dateString)
}
showTime={{
use12Hours: true,
defaultValue: moment('00:00:00', 'HH:mm:ss'),
}}
/>
<StyledDatePicker
format={dateFormat}
disabledDate={disabledToDate}
placeholder={'To'}
onChange={(todate, dateString) => onPageToDate(todate, dateString)}
showTime={{ use12Hours: true }}
/>
The above code is the index file for all the components,we'll be passing clearfilter prop,if its true ,the filter components are set to empty.The StyledDatePicker is just wrapped in styled components of some custom width.that's it.
You can clearly see,onChangeFromDate() and onPageToDate() are the event functions happening on Change,onChange. As I said above,I'm setting the setFromDate('') and setTodate('') when clearFilter is true.
To give some context,this another main file,from which the props are passed to the others.In there,we are defining setFromDate('') and setTodate('') as,
const [fromDate, setFromDate] = useState('')
const [toDate, setToDate] = useState('')
I think I've given enough details. If need anything, request, I'm ready to give. This is a live project, I'm stuck for a week.Thanks in advance!

How to extend the default behaviour of a prop of a React component?

Hello I have table component taken from ant design's table and I want to change what happens when you change your current page.
function DefaultTable<T extends Entity>(props: TableProps<T>): JSX.Element {
const { pagination } = props;
const [currentPage, setCurrentPage] = useState(1);
const [currentPageSize, setCurrentPageSize] = useState<number>();
return (
<Form>
<Table
{...props}
pagination={
pagination !== false && {
onChange: e => setCurrentPage(e),
defaultCurrent: currentPage,
onShowSizeChange: (_current, newSize) => setCurrentPageSize(newSize),
pageSize: currentPageSize,
...pagination
}
}
/>
</Form>
);
}
However, when I change the page, the filters, sorters and some other configurations are also gone. I think it is because of this onChange function onChange: e => setCurrentPage(e), the default behaviour is ignored. Is there a way to extend the default onChange, and then add my current setCurrentPage(e) to it?
I've been looking on Table and Pagination implementation on antd and there is no evidence which explains why adding onChange would prevent the default behavior.
https://codesandbox.io/s/customized-filter-panel-antd4123-forked-1nwro?file=/index.js
I also have been playing around the Table example provided by antd docs, where I add a onChange handle and the filtering behavior remains the same.
Try to isolate the code and provide more info - this way we could help you better.

React - problems with useState and Firebase

I was trying to resolve this problem, but I have no luck...
I'm using React and 'react-bootstrap'. Getting data from firebase with useState, as you can see in the next code. But also I'm calling a modal as a component, and this modal use useState to show and hide the modal.
export const Models = () => {
const [models, setModels] = useState(null);
useEffect(() => {
firebase.database().ref('Models').on('value', (snapshot) => {
setModels(snapshot.val())
});
}, []);
return models;
}
the problem result when I click on the url to access the modal, this one is shown and the main component goes to firebase and tries to get the data again. So, if I click 3 times on the modal, I will get my data from firebase 3 times.
How can I fix this? to get my data from firebase only one time, regardless of the times that you open the modal window?
The other part of the code
const Gallery = () => {
const [fireBaseDate, setFireBaseDate] = useState(null);
axios.post('https://us-central1-models-gallery-puq.cloudfunctions.net/date',{format:'DD/MM/YYYY'})
.then((response) => {
setFireBaseDate(response.data)
});
let content = Models();
let models = [];
const [imageModalShow, setImageModalShow] = useState(false);
const [selectedModel, setSelectedModel] = useState('');
if(content){
Object.keys(content).map((key, index) =>
models[index] = content[key]
);
models = shuffleArray(models);
console.log(models)
return(
<div className="appContentBody">
<Jumbo />
<Promotion models={models}/>
<div className="Gallery">
<h1>Galería - Under Patagonia</h1>
<Filter />
<div className="img-area">
{models.map((model, key) =>{
let myDate = new Date(model.creationDate);
let modelEndDate = new Date(myDate.setDate(myDate.getDate() + 30)).toLocaleDateString('en-GB')
if(fireBaseDate !== modelEndDate && model.active === true){
return (
<div className="img-card filterCard" key={key}>
<div className="flip-img">
<div className="flip-img-inner">
<div className="flip-img-front">
<img className="single-img card-img-top" src={model.thumbnail} alt="Model"/>
</div>
<div className="flip-img-back">
<h2>{model.certified ? 'Verificada!' : 'No Verificada'}</h2>
<p>Número: {model.contact_number}</p>
<p>Ciudad: {model.city}</p>
<p>Servicios: {model.services}</p>
</div>
</div>
</div>
<h5>{model.name}</h5>
<Button variant="danger" onClick={() => {
setImageModalShow(true)
setSelectedModel(model)}
}>
Ver
</Button>
</div>
);
}
return 0})}
</div>
<Image
show={imageModalShow}
onHide={() => setImageModalShow(false)}
model={selectedModel}
/>
</div>
<Footer />
</div>
)} else {
return (
<div className="loading">
<h1>Loading...</h1>
</div>
)}
}
export default Gallery;
Thanks for your time!
Models is a regular javascript function, not a functional component. So this is not a valid use of hooks, and will not work as expected. See docs on rules of hooks.
A functional component receives props and returns JSX or another React element.
Since it does not, it is basically restarting and calling your effect each time its called by the parent.
Looking at your edit, you should probably just remove the Models function and put the logic in the Gallery component.
The way I read your above component makes it seem like you've defined a custom hook for getting data from firebase.
So first off, I would rename it to useFbData and treat it as a custom hook, so that you can make use of the ESLint Plugin for Hooks and make sure you're following the rules of hooks.
The way you have this above, if it's a function within a component, your function will fire on every render, so the behaviour you are describing is what I would expect.
Depending on how expensive your request is/how often that component renders, this might be what you want, as you probably don't want to return stale data to your component. However, if you feel like the response from the DB should be cached and you have some logic to invalidate that data you could try something like this:
import { useEffect, useRef } from 'react';
const useFbData = invalidationFlag => {
const data = useRef(null);
useEffect(() => {
if (!data.current || invalidationFlag) {
firebase.database().ref('Data').on('value', (snapshot) => {
data.current = snapshot.val();
});
}
}, [invalidationFlag]);
return data.current;
};
export default useFbData;
This way, on the initial run and every time you changed the value of invalidationFlag, your effect inside the useFbData hook would run. Provided you keep track of invalidationFlag and set it as required, this could work out for you.
The reason I used a ref here instead of state, is so that the effect hook doesn't take the data in the dependency array (which would cause it to loop indefinitely if we used state).
This will persist the result of the db response between each call and prevent the call being made multiple times until you invalidate. Remember though, this will mean the data you're using is stale until you invalidate.

Categories