I’m developing a React component ClinicalViewsSummary that retrieves patient encounter data using the useSWR hook. The child component EncounterValuesTile fetches data with various params it has but sometimes receives undefined values, even when the params have not changed, and the data exists. Based on research, hooks should not do data fetching on the bottom level, but i cant avoid it in this case. As a result of looping, the hook is called multiple times before previous requests are resolved and some data is undefined
My main component looks like this :
const ClinicalViewsSummary: React.FC<OverviewListProps> = ({ patientUuid }) => {
const config = useConfig();
const { t } = useTranslation();
const tilesDefinitions1 = config.tilesDefinitions;
const tilesDefinitions = useMemo(
() => [
{
tileHeader: 'characteristicsTitle',
columns: [
{
id: 'currentRegimen',
hasSummary: true,
title: 'currentRegimenTitle',
encounterType: 'e22e39fd-7db2-45e7-80f1-60fa0d5a4378',
concept: '164515AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
summaryConcept: {
primaryConcept: '164515AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
},
{
id: 'artCohort',
title: 'ArtCohortTitle',
encounterType: 'e22e39fd-7db2-45e7-80f1-60fa0d5a4378',
concept: '159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
isDate: true,
conceptMappings: [
'159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
'162572AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
'164516AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
'164431AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
'160738AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
],
},
{
id: 'dsdModel',
title: 'dSDModelTitle',
hasSummary: true,
encounterType: 'e22e39fd-7db2-45e7-80f1-60fa0d5a4378',
concept: 'dfbe256e-30ba-4033-837a-2e8477f2e7cd',
summaryConcept: {
primaryConcept: '166448AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
},
{
id: 'populationType',
title: 'populationTypeTitle',
encounterType: 'e22e39fd-7db2-45e7-80f1-60fa0d5a4378',
concept: '164431AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
hasSummary: true,
summaryConcept: {
primaryConcept: '166433AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
secondaryConcept: '2bf14240-b2b2-42b2-8cf3-b5f8a0cb7764',
},
},
],
},
{
tileHeader: 'hivMonitoring',
columns: [
{
id: 'viralLoad',
title: 'currentViralLoad',
encounterType: '3596fafb-6f6f-4396-8c87-6e63a0f1bd71',
concept: '1305AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
hasSummary: true,
summaryConcept: {
primaryConcept: '163724AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
isDate: true,
},
},
{
id: 'lastCD4Count',
title: 'lastCD4CountTitle',
hasSummary: true,
encounterType: '3596fafb-6f6f-4396-8c87-6e63a0f1bd71',
concept: '5497AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
summaryConcept: {
primaryConcept: '163724AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
isDate: true,
},
},
],
},
{
tileHeader: 'lastVisitDetails',
columns: [
{
id: 'nextAppointmentDate',
title: 'nextAppointmentDateTitle',
hasSummary: true,
encounterType: 'cb0a65a7-0587-477e-89b9-cf2fd144f1d4',
concept: '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
summaryConcept: {
primaryConcept: '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
isDate: true,
hasCalculatedDate: true,
},
},
{
id: 'programStatus',
title: 'programStatusTitle',
encounterType: 'a221448d-512b-4750-84bf-d29be9f802b3',
concept: '163105AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
],
},
],
[],
);
const tilesData = useMemo(
() =>
tilesDefinitions?.map((tile: any) => ({
title: tile.tileHeader,
columns: getEncounterTileColumns(tile, t),
})),
[],
);
return (
<>
{tilesData?.length > 0 &&
tilesData?.map((tile, index) => (
<MemoizedEncounterTile
key={index}
patientUuid={patientUuid}
columns={tile.columns}
headerTitle={tile.title}
/>
))}
</>
);
};
My child components that display columns in different tiles look like this :
const EncounterTile: React.FC<EncounterTileProps> = ({ patientUuid, columns, headerTitle }) => {
return (
<div className={styles.tileContainer}>
<Tile className={styles.tile}>
<div className={styles.cardTitle}>
<h4 className={styles.title}> {headerTitle} </h4>
</div>
<Column className={styles.columnContainer}>
{columns.map((column, ind) => (
<EncounterValuesTile key={ind} patientUuid={patientUuid} column={column} />
))}
</Column>
</Tile>
</div>
);
};
export const MemoizedEncounterTile = React.memo(EncounterTile);
export const EncounterValuesTile: React.FC<EncounterValuesTileProps> = ({ patientUuid, column }) => {
const { lastEncounter, isLoading, error, isValidating } = useLastEncounter(patientUuid, column.encounterUuid);
if (isLoading || isValidating) {
return <CodeSnippetSkeleton type="multi" data-testid="skeleton-text" />;
}
if (error || lastEncounter === undefined) {
return (
<div className={styles.tileBox}>
<div className={styles.tileBoxColumn}>
<span className={styles.tileTitle}> {column.header} </span>
<span className={styles.tileValue}>--</span>
</div>
</div>
);
}
return (
<div className={styles.tileBox}>
<div className={styles.tileBoxColumn}>
<span className={styles.tileTitle}> {column.header} </span>
<span className={styles.tileValue}>
<LazyCell lazyValue={column.getObsValue(lastEncounter)} />
</span>
{column.hasSummary && (
<span className={styles.tileTitle}>
<LazyCell lazyValue={column.getSummaryObsValue(lastEncounter)} />
</span>
)}
</div>
</div>
);
};
How can I ensure that these requests are made sequentially, so that I only fetch data when the previous fetch has completed?
Is there a way to optimize my hooks to prevent multiple requests for the same encounter type?
Are there best practices to handle this kind of situation in React when using hooks like useSWR?
Any advice or solutions would be greatly appreciated!