Skip to content

Commit

Permalink
(refactor) Refactor usePatientPrograms hook to use SWR
Browse files Browse the repository at this point in the history
Refactors the usePatientProgram hook to leverage an SWR hook for fetching patient programs. With this, we get:

- Automatic caching and revalidation
- Conditional fetching based on the patientUuid and the `formJson.meta.programs.hasProgramFields` property of the form
- Built-in error and loading states
- Better handling of race conditions
  • Loading branch information
denniskigen committed Dec 11, 2024
1 parent 611e050 commit e9e89b7
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 45 deletions.
52 changes: 29 additions & 23 deletions src/hooks/usePatientPrograms.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import useSWR from 'swr';
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { useEffect, useState } from 'react';
import { type FormSchema, type PatientProgram } from '../types';
const customRepresentation = `custom:(uuid,display,program:(uuid,name,allWorkflows),dateEnrolled,dateCompleted,location:(uuid,display),states:(startDate,endDate,state:(uuid,name,retired,concept:(uuid),programWorkflow:(uuid)))`;
import type { FormSchema, ProgramsFetchResponse } from '../types';

export const usePatientPrograms = (patientUuid: string, formJson: FormSchema) => {
const [patientPrograms, setPatientPrograms] = useState<Array<PatientProgram>>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const useActiveProgramEnrollments = (patientUuid: string) => {
const customRepresentation = `custom:(uuid,display,program:(uuid,name,allWorkflows),dateEnrolled,dateCompleted,location:(uuid,display),states:(startDate,endDate,state:(uuid,name,retired,concept:(uuid),programWorkflow:(uuid)))`;
const apiUrl = `${restBaseUrl}/programenrollment?patient=${patientUuid}&v=${customRepresentation}`;

const { data, error, isLoading } = useSWR<{ data: ProgramsFetchResponse }, Error>(
patientUuid ? apiUrl : null,
openmrsFetch,
);

const sortedEnrollments =
data?.data?.results.length > 0
? data?.data.results.sort((a, b) => (b.dateEnrolled > a.dateEnrolled ? 1 : -1))
: null;

useEffect(() => {
if (formJson.meta?.programs?.hasProgramFields) {
openmrsFetch(`${restBaseUrl}/programenrollment?patient=${patientUuid}&v=${customRepresentation}`)
.then((response) => {
setPatientPrograms(response.data.results.filter((enrollment) => enrollment.dateCompleted === null));
setIsLoading(false);
})
.catch((error) => {
setError(error);
setIsLoading(false);
});
} else {
setIsLoading(false);
}
}, [formJson]);
const activePrograms = sortedEnrollments?.filter((enrollment) => !enrollment.dateCompleted);

return {
patientPrograms,
activePrograms,
error,
isLoading,
};
};

export const usePatientPrograms = (patientUuid: string, formJson: FormSchema) => {
const { activePrograms, error, isLoading } = useActiveProgramEnrollments(
formJson.meta?.programs?.hasProgramFields ? patientUuid : null,
);

return {
patientPrograms: activePrograms,
errorFetchingPatientPrograms: error,
isLoadingPatientPrograms: isLoading,
};
};
41 changes: 19 additions & 22 deletions src/processors/encounter/encounter-form-processor.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import {
type FormField,
type FormPage,
type FormProcessorContextProps,
type FormSchema,
type FormSection,
type ValueAndDisplay,
} from '../../types';
import { usePatientPrograms } from '../../hooks/usePatientPrograms';
import { useEffect, useState } from 'react';
import { useEncounter } from '../../hooks/useEncounter';
import { isEmpty } from '../../validators/form-validator';
import { type FormContextProps } from '../../provider/form-provider';
import { FormProcessor } from '../form-processor';
import { type OpenmrsResource, showSnackbar, translateFrom } from '@openmrs/esm-framework';
import {
getMutableSessionProps,
hydrateRepeatField,
Expand All @@ -23,23 +11,32 @@ import {
savePatientIdentifiers,
savePatientPrograms,
} from './encounter-processor-helper';
import { type OpenmrsResource, showSnackbar, translateFrom } from '@openmrs/esm-framework';
import { moduleName } from '../../globals';
import {
type FormField,
type FormPage,
type FormProcessorContextProps,
type FormSchema,
type FormSection,
type ValueAndDisplay,
} from '../../types';
import { evaluateAsyncExpression, type FormNode } from '../../utils/expression-runner';
import { extractErrorMessagesFromResponse } from '../../utils/error-utils';
import { extractObsValueAndDisplay } from '../../utils/form-helper';
import { FormProcessor } from '../form-processor';
import { getPreviousEncounter, saveEncounter } from '../../api';
import { useEncounterRole } from '../../hooks/useEncounterRole';
import { evaluateAsyncExpression, type FormNode } from '../../utils/expression-runner';
import { hasRendering } from '../../utils/common-utils';
import { extractObsValueAndDisplay } from '../../utils/form-helper';
import { isEmpty } from '../../validators/form-validator';
import { moduleName } from '../../globals';
import { type FormContextProps } from '../../provider/form-provider';
import { useEncounter } from '../../hooks/useEncounter';
import { useEncounterRole } from '../../hooks/useEncounterRole';
import { usePatientPrograms } from '../../hooks/usePatientPrograms';

function useCustomHooks(context: Partial<FormProcessorContextProps>) {
const [isLoading, setIsLoading] = useState(true);
const { encounter, isLoading: isLoadingEncounter } = useEncounter(context.formJson);
const { encounterRole, isLoading: isLoadingEncounterRole } = useEncounterRole();
const { isLoading: isLoadingPatientPrograms, patientPrograms } = usePatientPrograms(
context.patient?.id,
context.formJson,
);
const { isLoadingPatientPrograms, patientPrograms } = usePatientPrograms(context.patient?.id, context.formJson);

useEffect(() => {
setIsLoading(isLoadingPatientPrograms || isLoadingEncounter || isLoadingEncounterRole);
Expand Down
4 changes: 4 additions & 0 deletions src/types/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ export interface PatientProgram extends OpenmrsResource {
states?: Array<ProgramWorkflowState>;
}

export interface ProgramsFetchResponse {
results: Array<PatientProgram>;
}

export interface PatientProgramPayload {
program?: string;
uuid?: string;
Expand Down

0 comments on commit e9e89b7

Please sign in to comment.