PrimeReact - Problem with datatable and lazy loading - reactjs

I have a problem with DataTable and lazy loading.
When I activate the lazy loading paramater (which i can't remove because the request from the api is too big) the filtering and sorting does not work when the lazy loading is activated. I understand why it works like that, but I really have no idea how to fix it.
If you have any solutions I take it beacause I'm really stuck here :)
link to codesandbox -> https://codesandbox.io/s/datatable-t92wk
I made some modifications with the base exemple in primereact like adding lazy loading, removing paggination and replace it by virtual scrolling. I also made a modification with the filtering but I think that what I did is not really correct, but it works in that case : it stops the lazy variable and therefore I can use lazy loading.
What I would love to do is to sort already loaded data in the table :)
Thank you

Had the same issue. Apparently you can't use filtering with lazy loading mode (link). Makes sense really.
For sorting, you could pass an onSort function and everytime a sort event happens (i.e: user clicks on the column header), you can send back a DB request.
const onSort = (e) => {
// use e.sortField and e.sortOrder to send a DB request,
}
<DataTable onSort={onSort} ... // rest of the stuff />

Related

ReactQuery - useInfiniteQuery refetching issue

I have implemented infinite scroll on a project that is using React Query library.
So far so good. Everything works as expected using the useInfiniteScroll hook
One issue that I am encountering is the caching mechanism of this library.
If I query for a resource, ex: GET /posts/, will trigger a GET /posts/?page=0, scroll a bit down, get the next results, GET /posts/?page=1 which is totally fine. If I add search parameter to the page will do a GET /posts/?filter=someValue&page=0. All fine... but when I remove the filter from the search it will automatically do GET /posts/?page=0 & GET /posts/?page=1
A solution is to remove the query from the cache using the remove() method from the query itself. But this means that I have to manually do it for each query.
I would like to get a better solution that will cover all cases. I have a queryWrapper where I want to handle this.
I tried using the queryClient instances invalidateQueries and resetQueries but none seems to be able to remove it from the cache...
In the examples below I keep a ref for the params, if they are changed I can trigger the query to reset via useLayoutEffect hook. This part works as expected
invalidateQueries attempt
queryClient.invalidateQueries(
[queryKey, JSON.stringify(paramsRef.current)],
{
exact: true,
refetchActive: false,
refetchInactive: false
},
{ cancelRefetch: true }
);
resetQueries attempt
queryClient
.resetQueries([queryKey, JSON.stringify(paramsRef.current)], {
refetchPage: (page, index) => index === 0
})
I even tried the queryClient.clear() method which should remove all existing queries from the cache but still the page number somehow remains cached... I access the queryClient using useQueryClient hook. If I inspect it, I can see the queries inside.
Can someone please help me to sort this cache issue
Thanks!
but when I remove the filter from the search it will automatically do GET /posts/?page=0 & GET /posts/?page=1
This is the default react-query refetching behaviour: An infinite query only has one cache entry, with multiple pages. When a refetch happens, all currently visible pages will be refetched. This is documented here.
This is necessary for consistency, because if for example one entry was removed from page 1, and we wouldn't refetch page 2, the first entry on page 2 (old) would be equal to the last entry of page 1 (new).
Now the real question is, do you want a refetch or not when the filter changes? If not, then setting a staleTime would be the best solution. If you do want a refetch, refetching all pages is the safest option. Otherwise, you can try to remove all pages but the first one with queryClient.setQueryData when your query unmounts. react-query won't do that automatically because why would we delete data that the user has scrolled down to to see already.
Also note that for imperative refetches, like invalidateQueries, you have the option to pass a refetchPage function, where you can return a boolean for each page to indicate if it should be refetched or not. This is helpful if you only update one item and only want to refetch the page where this item resides. This is documented here.

How to hide or remove an element onClick in React?

I am trying to hide an element 'GorillaSurfIn' after I click on it.
But also it should fire the 'setShouldGorillaSurfOut' to 'true'. The second part works, but after I added this function:
function hideGorillaSurfIn() {
let element = document.getElementById('gorilla-surf-in');
ReactDOM.findDOMNode(element).style.display =
this.state.isClicked ? 'grid' : 'none';
}
After I click, the code falls apart.
Once I click, the element should be hidden/removed till the next time the App restarts.
Here is the Code Sandbox Link for further explanation.
I am open to any solutions, but also explanations please, as I am still fresh in learning React.
I have changed your code a bit to make it work. You can make further changes according to your need. A few things that I would like to add: -
You should avoid using findDOMNode (in most cases refs can solve your problem) as there are certain drawbacks associated with findDOMNode, such as the react's documentation states "findDOMNode cannot be used with functional components".
I've used refs (forward ref in this case) to make it work.
GorillaSurfIn was called twice, so there were two Gorilla gifs on the screen with same IDs. Not sure if that was the intended behaviour but each element should have unique ID.
Check out the code sandbox.

react-testing-library doesn't render all parts of a VirtualTable

CodeSandbox with Unit Tests
The CodeSandbox above contains a small code segment that shows the problem. The rendered table contains a bit of data. Everything on the left side is rendered and around the center, #testing-library/react stops rendering anything.
Visually, everything is fine. But the HTML rendered by #testing-library/react misses the entire right half of the table.
You can even see this if you hard reload the tab with CodeSandbox in it, then navigate to the "Browser" tab. The right half will be missing. Click the Browser refresh button in CodeSandbox and everything will be rendered just fine.
Without VirtualTable, everything renders perfectly. However, I need VirtualTable for performance reasons.
Extending the viewport programmatically doesn't help.
// setupTests.js
global.resizeWindow = (x, y) => {
window.innerWidth = x
window.innerHeight = y
window.dispatchEvent(new Event('resize'))
}
// In the test
global.resizeWindow(4096, 2160)
// Same result, no change
Is there any way to keep the benefits of VirtualTable while still running tests on the data contained on the right side of the table?
Here's the response from DevExpress:
Hi,
Thank you for your sample. I have discussed this behavior with our
developers and we concluded that this is the expected behavior. The
VirtualTable plugin renders only visual parts of the Grid control. So,
we recommend that you use the Table plugin for tests and the
VirtualTable plugin in a real application.
Thanks, Alessandro
So it would appear that since we don't have a real browser, the VirtualTable sees no need to render anything.
The solution would be to render a regular Table in a testing scenario, and a VirtualTable in a real-world scenario.
function areWeTestingWithJest() {
return process.env.JEST_WORKER_ID !== undefined;
}
However, using testing code in production code is usually considered bad practice, so I'll just avoid testing VirtualTables and test the data in other places instead.

need help for prev and next button in react

How to call back the effect operation when clicking the prev button using method or function with fullcalendar4 in react?
At present, I can only think of this way:
componentDidMount(){
document.querySelector('.fc-prev-button').addEventListener('click', e => {console.log(e)})
}
componentDidMount(){
document.querySelector('.fc-prev-button').addEventListener('click', e => {console.log(e)})
}
Looks like it works with React. Have you read some docs here about callbacks, or here specifically working with react and the underlying calendar's ref?
I've created a simple code sandbox that uses the callback style you try, and also using the API. Between the two, your method seems less recommended, but IMO is a little cleaner as it uses all the existing in-place UI, but is susceptible to being a little more brittle as the class names could potentially change, whereas using the API for next/prev these are calling methods directly so less likely to change.
I guess it depends a lot on what exactly you're trying to accomplish here, but what you have will "piggyback" off the button click, so if you're just trying to do something on the side it'll work.

ReactJs Lazy loader

I'm new in ReactJs.
I need a lazy loader in my application when page is scroll down,
I'm using (https://jasonslyvia.github.io/react-lazyload/examples/#/normal?_k=rz3oyn)
Actually, this is working fine but first time it load all data.
I want to make api call and set data when page will scroll down.
Thank you.
Most probably what you need is an infinite scroll functionality. There are some good options for React when it comes to loading asynchronous data:
1) If you need high performance (a lot of results coming from the server) you can try react-infinite. As docs say:
React Infinite solves this by rendering only DOM nodes that the user
is able to see or might soon see.
2) Another simple option would be: react-infinite-scroller
General principle to make these libraries work is to pass their props which will let the module know when to make a request or when to wait.
<InfiniteScroll
pageStart={0}
loadMore={loadFunc}
hasMore={true || false}
loader={<div className="loader">Loading ...</div>}
>
{items} // <-- This is the content you want to load
</InfiniteScroll>
loadFunc will make an API call to the server and will set hasMore to false until it is successfully resolved.

Resources