How to achieve Infinite scroll in React with React Query? - infinite-scroll

I want my API to fetch new data as in increase the page number when I have reached the bottom of the page, basically I want to infinite scroll with React Query.
I have literally watched all the videos out there but am still unable to figure out why isn't it working. Please help me out.
events: (30) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
hasNextPage: true
There's no mention of the number of pages available in this API, or the total number of events available. The only way to check if there is a next page is through this hasNextPage: true property which returns either true or false
So I have no clue on how to do that using React-Query, I have tried multiple approaches. Can anyone please help me out on how to achieve Infinite scroll using React-Query with this data from the API.

useInfiniteQuery
If your API response data doesn't have any page info, you can use the second parameter returned from getNextPageParam to achieve your needs. This parameter returns an array containing all the fetched page content, so you can use its length as a basis.
Sample code:
const {
data, isLoading, fetchNextPage, hasNextPage,
} = useInfiniteQuery({
queryKey: ['someKey'],
queryFn: fetchData,
// here
getNextPageParam: (lastPage, pages) => pages.length + 1,
});

You can use the npm package react-infinite-scroll-component along with useInfiniteQuery hook.
This article demonstrates it in detail.

You have work with useInfiniteQuery and hasNextPage would be enough.
Query:
const { isFetchingNextPage, fetchNextPage, hasNextPage, data } = useInfiniteQuery(
['some-key' ,page, size],
({ pageParam = 0 }) => getList({ page: pageParam, ...params }),
{
getNextPageParam: (lastPage) => (!lastPage.last ? lastPage.number + 1 : undefined),
},
);
const inViewport = useInViewport(ref);
useEffect(() => {
if (inViewport) fetchNextPage();
}, [inViewport]);
And your list would be like this:
<div>
{data?.pages.map((page) => (
<React.Fragment key={page.number}>
{page.events.map(renderItem)}
</React.Fragment>
))}
{hasMore && (
<div className={styles.loading} ref={ref}>
{isFetchingNextPage && <Spinner/>}
</div>
)}
</div>

You can implement it using the component PerfectScrollbar from this package
https://www.npmjs.com/package/react-perfect-scrollbar
and by using the prop onYReachEnd you can call a function that first check if hasNextPage is true then you will need to check that you are not fetching data currently in order to not fetch all pages at same time, you can do that by check that by checking loadingGetData is false

Related

Esri-leaflet-geocoder: Component not rendering; How to connect providers in Production

I am using the great esri-leaflet-geocoder plugin and can't get it to render in production.
I registered for a provider (ArcGIS Online Geocoding Service) and got a api key, and followed the documentation on the github page to add the api key:
var searchControl = L.esri.Geocoding.geosearch({
providers: [
L.esri.Geocoding.arcgisOnlineProvider({
// API Key to be passed to the ArcGIS Online Geocoding Service
useMapBounds: false,
apikey: process.env.ESRI_API_KEY
})
]
});
I was getting the following error:
TypeError: Cannot read property 'Geocoding' of undefined
So with that I went to the official documentation page of esri-leaflet-geocoder here and tried what was listed there. Turns out it seems more up to date.
var provider = ELG.arcgisOnlineProvider({ token: process.env.ESRI_API_KEY });
var searchControl = new ELG.Geosearch({
useMapBounds: false,
providers: [provider]
});
console.log('ELG.arcgisOnlineProvider() ', provider);
console.log('searchControl', searchControl);
It didn't work but the consoles seems to show that they indeed take the props listed in the documentation:
ELG.arcgisOnlineProvider()
NewClass {_requestQueue: Array(0), _authenticating: false, options: {…}, _initHooksCalled: true, _eventParents: {…}}
options:
supportsSuggest: true
token: process.env.ESRI_API_KEY // In the log it's a string
url: "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/"
__proto__: Object
_authenticating: false
_eventParents: {1: NewClass}
_initHooksCalled: true
_requestQueue: []
__proto__: NewClass
searchControl
NewClass {options: {…}, _geosearchCore: NewClass, _leaflet_id: 1, _initHooksCalled: true}
options:
providers: Array(1)
0: NewClass
options: {token: process.env.ESRI_API_KEY // Again it is logging a string
url: "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/", supportsSuggest: true}
_authenticating: false
_eventParents: {1: NewClass}
_initHooksCalled: true
_requestQueue: []
__proto__: NewClass
length: 1
__proto__: Array(0)
useMapBounds: false
__proto__: Object
So how can I get the 'searchControl' to work/render in Production?
This is working for me:
import * as ELG from "esri-leaflet-geocoder";
var searchControl = ELG.geosearch({
useMapBounds: false,
providers: [
ELG.arcgisOnlineProvider({
apikey: "your key here"
})
]
});
searchControl.addTo(leafletMap);
Working codesandbox

Can Protractor ignores the timeouts of 3rd party plugin in angular?

I'm using amchart to show my data analytics in the Angular app. If I run an E2E test on that page that contains the amchart plugin, it's not able to finish (script timeout) cause it using real-time updates for charts (dynamic)...
This command 'getAllAngularTestabilities()' in console shows that has been pendingMacrotasks on page, so if the Protractor not working here, it's totally okay.
[Testability]
0: Testability
taskTrackingZone: TaskTrackingZoneSpec {name: "TaskTrackingZone", microTasks: Array(0), macroTasks: Array(3), eventTasks: Array(474), properties: {…}}
_callbacks: []
_didWork: true
_isZoneStable: true
_ngZone: NgZone
hasPendingMacrotasks: true
hasPendingMicrotasks: false
isStable: true
lastRequestAnimationFrameId: -1
nativeRequestAnimationFrame: ƒ requestAnimationFrame()
onError: EventEmitter_ {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
onMicrotaskEmpty: EventEmitter_ {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
onStable: EventEmitter_ {_isScalar: false, observers: Array(2), closed: false, isStopped: false, hasError: false, …}
onUnstable: EventEmitter_ {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
shouldCoalesceEventChangeDetection: false
I have 3 charts on that page, so I checked what NgZone says: Coming to a request in every second, and I can't turn off them. I'm trying to find the solution in amchart's documentation but I haven't found anything yet...
ZONE pending tasks=
(3) [ZoneTask, ZoneTask, ZoneTask]
0: ZoneTask
callback: ƒ ()
cancelFn: undefined
creationLocation: Error: Task 'macroTask' from 'setTimeout'. at TaskTrackingZoneSpec.push.FGvd.TaskTrackingZoneSpec.onScheduleTask (http://localhost:4200/vendor.js:54102:40) at ZoneDelegate.scheduleTask (http://localhost:4200/polyfills.js:9471:55) at Object.onScheduleTask (http://localhost:4200/polyfills.js:9365:69) at ZoneDelegate.scheduleTask (http://localhost:4200/polyfills.js:9471:55) at Zone.scheduleTask (http://localhost:4200/polyfills.js:9303:47) at Zone.scheduleMacroTask (http://localhost:4200/polyfills.js:9326:29) at scheduleMacroTaskWithCurrentZone (http://localhost:4200/polyfills.js:10227:29) at http://localhost:4200/polyfills.js:11679:34 at proto.<computed> (http://localhost:4200/polyfills.js:10542:52) at loop_1 (http://localhost:4200/vendor.js:23731:42)
data: {isPeriodic: false, delay: 1000, args: Arguments(2), handleId: 1516}
invoke: ƒ ()
runCount: 0
scheduleFn: ƒ scheduleTask(task)
source: "setTimeout"
type: "macroTask"
_state: "notScheduled"
_zone: Zone {_parent: Zone, _name: "angular", _properties: {…}, _zoneDelegate: ZoneDelegate}
_zoneDelegates: null
state: (...)
zone: (...)
__proto__: Object
1: ZoneTask {_zone: Zone, runCount: 0, _zoneDelegates: null, _state: "notScheduled", type: "macroTask", …}
2: ZoneTask {_zone: Zone, runCount: 0, _zoneDelegates: null, _state: "notScheduled", type: "macroTask", …}
length: 3
__proto__: Array(0)
UPDATE!
I can avoid this problem with a tiny workaround.
Need to create a function that using runOutsideAngular(), and if I create the chart inside the callback, no will be running macrotasks!
constructor(#Inject(PLATFORM_ID) private platformId, private zone: NgZone) {
}
// Run the function only in the browser
browserOnly(f: () => void): void {
if (isPlatformBrowser(this.platformId)) {
this.zone.runOutsideAngular(() => {
f();
});
}
}
ngOnInit(): void {
this.browserOnly(() => {
this.chart = am4core.create('line-chart-placeholder', am4charts.XYChart);
});
}
yes, you can ignore it. You need to disable main protractor's feature that waits for page to be ready, like this
await browser.waitForAngularEnabled(false)
for more info, read here

axios patch updates website state, but it doesnt update mongo db state

I have been trying to edit/update product on my website trough axios patch method. When i submit my form i get status 200 OK , and data on the website is updated, until i refresh the page. When i refresh it old values return and db is not updated, which means that there is a problem with communication with db.
I am using atlas mongodb, and i had similar problem with axios delete method, but the problem was in Atlas IP restrictions which i have solved now, but problem with axios patch remains.
my vuejs patch method:
handleEditForm(_id) {
axios.patch(`http://localhost:3000/api/ropes/${_id}`, this.ropes)
.then((res) => {
console.log(res)
this.showModal = false
}).catch(error => {
console.log(error)
})
},
}
server side patch:
router.patch('/:ropeId', async (req, res) => {
try {
const updatedRope = await Rope.updateOne({_id: req.params.ropeId}, {
$set: {
image:req.body.image,
title: req.body.title,
description: req.body.description,
diameter:req.body.diameter,
price:req.body.price,
cartQuantity:req.body.cartQuantity,
totalQuantity:req.body.totalQuantity
}
})
res.json(updatedRope)
} catch (err) {
res.json({message: err})
}
})
console log:
adapter: function xhrAdapter(config)
​​
data: "[{\"_id\":\"60171a1bd86adc454b3b757a\",\"title\":\"Polyester sinking ropes\",\"description\":\"Polyester sinking rope Polyester sinking rope Polyester sinking rope Polyester sinking rope Polyester sinking rope\",\"image\":\"\",\"diameter\":\"22mm\",\"price\":35,\"cartQuantity\":1,\"totalQuantity\":\"1000\",\"date\":\"2021-01-31T20:59:07.452Z\",\"__v\":0}]"
​​
Object { data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, request: XMLHttpRequest }
headers: Object { Accept: "application/json, text/plain, */*", "Content-Type": "application/json;charset=utf-8" }
​​
maxBodyLength: -1
​​
maxContentLength: -1
​​
method: "patch"
​​
timeout: 0
``

How to write a axios get command correctly to curruncy rate api?

I have a problem like this. I am new to react stuff. I used axios HTTP request npm package to write API call. But when I console log the response it says like this.
{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data:
error: {code: 101, type: "missing_access_key", info: "You have not supplied an API Access Key. [Required format: access_key=YOUR_ACCESS_KEY]"}
success: false
__proto__: Object
headers: {content-type: "application/json; Charset=UTF-8"}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: "OK"
__proto__: Object
It says that I am not providing API_key. This is how I have written my code.
const access_key ='my_key'
axios.get(`http://data.fixer.io/api/2013-12-24
? access_key =${access_key} & base = LKR & symbols = ETH`)
.then(res=>{
console.log(res);
})
Can Someone help me to solve this problem?. Thank You.
An alternative
In case you're feeling tired of using a quite long string url which easily causes mistakes, the axios library already supports an alternative. Using like this:
const access_key ='my_key'
axios.get('http://data.fixer.io/api/2013-12-24', {
params: {
access_key: access_key,
base: LKR,
symbols: ETH
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
For reference: https://github.com/axios/axios#example

Cache-control directive (only-if-cached) changed by dev-tools?

We are working on a Progessive Web App, for which the service worker intercepts the network traffic (via the fetch event handler). We have noticed, that sometimes a certain request fails here, because Request.cache is only-if-cached and Request.mode is no-cors, but not same-origin.
So it is similar to this problem.
Then I've noticed, that this happens only when the Chrome (v 65) DevTools are not opened. Does anybody notice the same phenomenon and does anybody have an idea, why this happens this way?
Parts of the request:
bodyUsed: false,
cache: "only-if-cached",
credentials: "include",
destination: "unknown",
headers: Headers {},
integrity: "",
method: "GET",
mode: "no-cors",
redirect: "follow",
referrer: "",
referrerPolicy: "no-referrer-when-downgrade",
url: "https://example.com/path/to/app-name/#!
We are handling this problem like this, but I'm afraid, that this is not appropriate.
serviceWorkerGlobal.addEventListener('fetch', function(event)
{
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
var oStrangeRequest = event.request.clone();
console.log('Fetch. Request cache has only-if-cached, but not same-origin.',
oStrangeRequest.cache, oStrangeRequest.mode,
'request redirect:',
oStrangeRequest.redirect, oStrangeRequest.url, oStrangeRequest);
return;
}
// ...
});
This is a bug. You can check the progress of the fix here: https://bugs.chromium.org/p/chromium/issues/detail?id=823392