Skip to content

Commit

Permalink
Merge pull request #1228 from VEuPathDB/1103-react-query-retrofit
Browse files Browse the repository at this point in the history
Use `useQuery` in place of `usePromise` for EDA subsetting and visualizations
  • Loading branch information
bobular authored Nov 19, 2024
2 parents 97d1f21 + 0415b50 commit 42431de
Show file tree
Hide file tree
Showing 13 changed files with 870 additions and 942 deletions.
17 changes: 17 additions & 0 deletions packages/libs/eda/src/lib/core/api/queryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { QueryClient } from '@tanstack/react-query';

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// This is similar behavior to our custom usePromise hook.
// It can be overridden on an individual basis, if needed.
keepPreviousData: true,
// We presume data will not go stale during the lifecycle of an application.
staleTime: Infinity,
// Do not attempt to retry if an error is encountered
retry: false,
// Do not referch when the browser tab is focused again
refetchOnWindowFocus: false,
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { getOrElse } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { number, partial, TypeOf, boolean, type, intersection } from 'io-ts';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { usePromise } from '../../hooks/promise';
import { useCachedPromise } from '../../hooks/cachedPromise';
import { AnalysisState } from '../../hooks/analysis';
import { useSubsettingClient } from '../../hooks/workspace';
import { DateRangeFilter, NumberRangeFilter } from '../../types/filter';
Expand Down Expand Up @@ -126,13 +126,11 @@ export function HistogramFilter(props: Props) {
getOrElse((): UIState => defaultUIState)
);
}, [variableUISettings, uiStateKey, defaultUIState]);
const uiStateForData = useDebounce(uiState, 1000);
const dataParams = useDebounce(uiState, 1000);
const subsettingClient = useSubsettingClient();

const getData = useCallback(
async (
dataParams: UIState
): Promise<
const data = useCachedPromise(
async (): Promise<
HistogramData & {
variableId: string;
entityId: string;
Expand Down Expand Up @@ -220,17 +218,14 @@ export function HistogramFilter(props: Props) {
};
},
[
dataParams,
otherFilters,
entity.displayName,
entity.displayNamePlural,
entity.id,
studyMetadata.id,
subsettingClient,
variable,
]
);
const data = usePromise(
useCallback(() => getData(uiStateForData), [getData, uiStateForData])
] // used to have `subsettingClient`
);

const filter = filters?.find(
Expand Down
147 changes: 72 additions & 75 deletions packages/libs/eda/src/lib/core/components/filter/MultiFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from '@veupathdb/wdk-client/lib/Components/AttributeFilter/Types';

import { AnalysisState } from '../../hooks/analysis';
import { usePromise } from '../../hooks/promise';
import { useSubsettingClient } from '../../hooks/workspace';
import { MultiFilter as MultiFilterType } from '../../types/filter';
import {
Expand All @@ -33,6 +32,7 @@ import { gray, red } from './colors';
import { debounce } from 'lodash';
import { isTableVariable } from './guards';
import { useDeepValue } from '../../hooks/immutability';
import { useCachedPromise } from '../../hooks/cachedPromise';

export interface Props {
analysisState: AnalysisState;
Expand Down Expand Up @@ -193,81 +193,78 @@ export function MultiFilter(props: Props) {
}, [_thisFilter, thisFilter]);

// Counts retrieved from the backend, used for the table display.
const leafSummariesPromise = usePromise(
useCallback(() => {
return Promise.all(
leaves.map((leaf) => {
const thisFilterWithoutLeaf = thisFilter && {
...thisFilter,
subFilters: thisFilter.subFilters.filter(
(f) => f.variableId !== leaf.term
),
};
return getDistribution(
{
entityId: entity.id,
variableId: leaf.term,
filters:
thisFilterWithoutLeaf == null ||
thisFilterWithoutLeaf.subFilters.length === 0 ||
thisFilterWithoutLeaf.operation === 'union'
? otherFilters
: [...(otherFilters || []), thisFilterWithoutLeaf],
},
(filters) =>
subsettingClient.getDistribution(
studyMetadata.id,
entity.id,
leaf.term,
{
filters,
valueSpec: 'count',
}
)
).then((distribution) => {
const fgValueByLabel = Object.fromEntries(
distribution.foreground.histogram.map(({ binLabel, value }) => [
binLabel,
value ?? 0,
])
);
const bgValueByLabel = Object.fromEntries(
distribution.background.histogram.map(({ binLabel, value }) => [
binLabel,
value ?? 0,
])
const leafSummariesPromise = useCachedPromise(() => {
return Promise.all(
leaves.map((leaf) => {
const thisFilterWithoutLeaf = thisFilter && {
...thisFilter,
subFilters: thisFilter.subFilters.filter(
(f) => f.variableId !== leaf.term
),
};
return getDistribution(
{
entityId: entity.id,
variableId: leaf.term,
filters:
thisFilterWithoutLeaf == null ||
thisFilterWithoutLeaf.subFilters.length === 0 ||
thisFilterWithoutLeaf.operation === 'union'
? otherFilters
: [...(otherFilters || []), thisFilterWithoutLeaf],
},
(filters) =>
subsettingClient.getDistribution(
studyMetadata.id,
entity.id,
leaf.term,
{
filters,
valueSpec: 'count',
}
)
).then((distribution) => {
const fgValueByLabel = Object.fromEntries(
distribution.foreground.histogram.map(({ binLabel, value }) => [
binLabel,
value ?? 0,
])
);
const bgValueByLabel = Object.fromEntries(
distribution.background.histogram.map(({ binLabel, value }) => [
binLabel,
value ?? 0,
])
);
const variable = variablesById[leaf.term];
if (variable == null || !isTableVariable(variable))
throw new Error(
`Could not find a categorical EDA variable associated with the leaf field "${leaf.term}".`
);
const variable = variablesById[leaf.term];
if (variable == null || !isTableVariable(variable))
throw new Error(
`Could not find a categorical EDA variable associated with the leaf field "${leaf.term}".`
);
return {
term: leaf.term,
display: leaf.display,
valueCounts: variable.vocabulary?.map((label) => ({
value: label,
count: bgValueByLabel[label],
filteredCount: fgValueByLabel[label] ?? 0,
})),
internalsCount:
distribution.background.statistics.numDistinctEntityRecords,
internalsFilteredCount:
distribution.foreground.statistics.numDistinctEntityRecords,
};
});
})
);
}, [
thisFilter,
otherFilters,
leaves,
entity.id,
subsettingClient,
studyMetadata.id,
variablesById,
])
);
return {
term: leaf.term,
display: leaf.display,
valueCounts: variable.vocabulary?.map((label) => ({
value: label,
count: bgValueByLabel[label],
filteredCount: fgValueByLabel[label] ?? 0,
})),
internalsCount:
distribution.background.statistics.numDistinctEntityRecords,
internalsFilteredCount:
distribution.foreground.statistics.numDistinctEntityRecords,
};
});
})
);
}, [
thisFilter ? thisFilter : { type: 'NO_FILTER' },
otherFilters,
leaves,
entity.id,
studyMetadata.id,
variablesById,
]);

// Sorted counts. This is done separately from retrieving the data so that
// updates to sorting don't incur backend requests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getOrElse, map } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { boolean, keyof, number, partial, string, type, TypeOf } from 'io-ts';
import { useCallback, useMemo } from 'react';
import { usePromise } from '../../hooks/promise';
import { AnalysisState } from '../../hooks/analysis';
import { useSubsettingClient } from '../../hooks/workspace';
import { Filter } from '../../types/filter';
Expand All @@ -18,6 +17,7 @@ import { gray, red } from './colors';
// import axis label unit util
import { variableDisplayWithUnit } from '../../utils/variable-display';
import { useDeepValue } from '../../hooks/immutability';
import { useCachedPromise } from '../../hooks/cachedPromise';

type Props = {
studyMetadata: StudyMetadata;
Expand Down Expand Up @@ -80,8 +80,8 @@ export function TableFilter({
(f) => f.entityId !== entity.id || f.variableId !== variable.id
)
);
const tableSummary = usePromise(
useCallback(async () => {
const tableSummary = useCachedPromise(
async () => {
const distribution = await getDistribution<DistributionResponse>(
{
entityId: entity.id,
Expand Down Expand Up @@ -127,7 +127,8 @@ export function TableFilter({
filteredEntitiesCount:
distribution.foreground.statistics.numDistinctEntityRecords,
};
}, [entity.id, variable, otherFilters, subsettingClient, studyMetadata.id])
},
[entity.id, variable, otherFilters, studyMetadata.id] // used to have `subsettingClient`
);
const activeField = useMemo(
() => ({
Expand Down
Loading

0 comments on commit 42431de

Please sign in to comment.