Skip to content

Commit

Permalink
useOpenmrsFetchall
Browse files Browse the repository at this point in the history
  • Loading branch information
kb019 committed Nov 21, 2024
1 parent f860e26 commit 6d99fbe
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 107 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"devDependencies": {
"@babel/core": "^7.11.6",
"@carbon/react": "~1.37.0",
"@openmrs/esm-framework": "^5.7.3-pre.2185",
"@openmrs/esm-framework": "next",
"@openmrs/esm-patient-common-lib": "next",
"@playwright/test": "1.45.2",
"@swc/core": "^1.2.165",
Expand Down Expand Up @@ -64,7 +64,7 @@
"jest-cli": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^15.2.1",
"openmrs": "^5.7.3-pre.2185",
"openmrs": "next",
"prettier": "^3.1.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
Expand Down
118 changes: 16 additions & 102 deletions packages/esm-service-queues-app/src/hooks/useQueueEntries.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { type FetchResponse, openmrsFetch, restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
import { type QueueEntry, type QueueEntrySearchCriteria } from '../types';
import useSWR from 'swr';
import { useCallback, useEffect, useMemo, useState } from 'react';
Expand Down Expand Up @@ -68,104 +68,22 @@ export function useMutateQueueEntries() {
};
}

export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: string = repString) {
// This manually implements a kind of pagination using the useSWR hook. It does not use useSWRInfinite
// because useSWRInfinite does not support with `mutate`. The hook starts by fetching the first page,
// page zero, waits until data is fetched, then fetches the next page, and so on.
//
// Fine so far. Where things get complicated is in supporting mutation. When a mutation is made, the
// SWR hook first returns stale data with `isValidating` set to false. At this point we say we are
// "waiting for mutate," because we have called mutate, but the useSWR hook hasn't updated properly
// for it yet. Next it returns stale data again, this time with `isValidating` set to true. At this
// point we say we are no longer waiting for mutate. Finally, it returns fresh data with `isValidating`
// again set to false. We may then update the data array and move on to the next page.
const [data, setData] = useState<Array<Array<QueueEntry>>>([]);
const [totalCount, setTotalCount] = useState<number>();
const [currentPage, setCurrentPage] = useState<number>(0);
const [currentSearchCriteria, setCurrentSearchCriteria] = useState(searchCriteria);
const [currentRep, setCurrentRep] = useState(rep);
const [pageUrl, setPageUrl] = useState<string>(getInitialUrl(currentRep, currentSearchCriteria));
const [error, setError] = useState<Error>();
const { mutateQueueEntries } = useMutateQueueEntries();
const [waitingForMutate, setWaitingForMutate] = useState(false);

const refetchAllData = useCallback(
(newRep: string = currentRep, newSearchCriteria: QueueEntrySearchCriteria = currentSearchCriteria) => {
setWaitingForMutate(true);
setCurrentPage(0);
setPageUrl(getInitialUrl(newRep, newSearchCriteria));
},
[currentRep, currentSearchCriteria],
);

// This hook listens to the searchCriteria and rep values and refetches the data when they change.
useEffect(() => {
const isSearchCriteriaUpdated = !isEqual(currentSearchCriteria, searchCriteria);
const isRepUpdated = currentRep !== rep;
if (isSearchCriteriaUpdated || isRepUpdated) {
if (isSearchCriteriaUpdated) {
setCurrentSearchCriteria(searchCriteria);
}
if (isRepUpdated) {
setCurrentRep(rep);
}
refetchAllData(rep, searchCriteria);
}
}, [searchCriteria, currentSearchCriteria, setCurrentSearchCriteria, currentRep, rep]);
export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: string = repString){

const { data: pageData, isValidating, error: pageError } = useSWR<QueueEntryResponse, Error>(pageUrl, openmrsFetch);
const [pageUrl, setPageUrl] = useState<string>(getInitialUrl(rep, searchCriteria));
const {data,mutate,...rest}=useOpenmrsFetchAll<any>(pageUrl);

useEffect(() => {
const nextUrl = getNextUrlFromResponse(pageData);
const stillWaitingForMutate = waitingForMutate && !isValidating;
if (waitingForMutate && isValidating) {
setWaitingForMutate(false);
}
if (pageData && !isValidating && !stillWaitingForMutate) {
// We've got results! Time to update the data array and move on to the next page.
if (pageData?.data?.totalCount > -1 && pageData?.data?.totalCount !== totalCount) {
setTotalCount(pageData?.data?.totalCount);
}
if (pageData?.data?.results) {
const newData = [...data];
newData[currentPage] = pageData?.data?.results;
setData(newData);
}
setCurrentPage(currentPage + 1);
setPageUrl(nextUrl);
// If we're mutating existing data, then we again need to wait for the mutate to work,
// since useSWR will (again) first return stale data with isValidating set to false.
const inMutateMode = data.length > currentPage;
if (inMutateMode && nextUrl) {
setWaitingForMutate(true);
}
}
// It may happen that there are fewer pages in the new data than in the old data. In this
// case, we need to remove the extra pages, which are stored on the `data` array.
// Note that since we mutated the `data` state earlier in this function, it is important to
// use the functional form of `setData` so as not to use the stale `data` state.
if (!nextUrl) {
// I will not be very suprised if there is an off-by-one error here.
if (data.length > currentPage + 1) {
setData((prevData) => {
const newData = [...prevData];
newData.splice(currentPage + 1);
return newData;
});
}
}
}, [pageData, data, currentPage, totalCount, waitingForMutate, isValidating]);
setPageUrl(getInitialUrl(rep, searchCriteria));
}, [searchCriteria,rep]);

useEffect(() => {
// An error to one is an error to all
if (pageError) {
setError(pageError);
}
}, [pageError]);
useEffect(()=>{
mutate();
},[pageUrl])

const queueUpdateListener = useCallback(() => {
refetchAllData();
}, [refetchAllData]);
mutate();
}, []);

useEffect(() => {
window.addEventListener('queue-entry-updated', queueUpdateListener);
Expand All @@ -174,16 +92,12 @@ export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep:
};
}, [queueUpdateListener]);

const queueEntries = useMemo(() => data.flat(), [data]);

return {
queueEntries,
totalCount,
isLoading: totalCount === undefined || (totalCount && queueEntries.length < totalCount),
isValidating: isValidating || currentPage < data.length,
error,
mutate: mutateQueueEntries,
};
queueEntries:data,
mutate,
...rest
}

}

export function useQueueEntriesMetrics(searchCriteria?: QueueEntrySearchCriteria) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function ClinicMetrics() {
/>
<MetricsCard
label={t('patients', 'Patients')}
value={initialSelectedItem ? totalCount ?? '--' : serviceCount}
value={initialSelectedItem ? totalCount || '--' : serviceCount}
headerLabel={`${t('waitingFor', 'Waiting for')}:`}
service={currentService?.serviceDisplay}
serviceUuid={currentService?.serviceUuid}
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3022,7 +3022,7 @@ __metadata:
"@carbon/react": "npm:~1.37.0"
"@hookform/resolvers": "npm:^3.3.1"
"@internationalized/date": "npm:^3.5.4"
"@openmrs/esm-framework": "npm:^5.7.3-pre.2185"
"@openmrs/esm-framework": "npm:next"
"@openmrs/esm-patient-common-lib": "npm:next"
"@playwright/test": "npm:1.45.2"
"@swc/core": "npm:^1.2.165"
Expand Down Expand Up @@ -3060,7 +3060,7 @@ __metadata:
jest-cli: "npm:^29.7.0"
jest-environment-jsdom: "npm:^29.7.0"
lint-staged: "npm:^15.2.1"
openmrs: "npm:^5.7.3-pre.2185"
openmrs: "npm:next"
prettier: "npm:^3.1.1"
react: "npm:^18.1.0"
react-dom: "npm:^18.1.0"
Expand Down

0 comments on commit 6d99fbe

Please sign in to comment.