mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 21:03:29 +00:00
refactor(views): use react-query's isLoadingError instead of reimplementing it
deriveQueryStatus previously inferred "never received data" by checking data !== undefined itself. TanStack Query already computes and exposes this exact distinction as isLoadingError (isError && no data) vs isRefetchError (isError && data present) on every useQuery result, so consume that directly instead of duplicating the logic.
This commit is contained in:
@@ -9,12 +9,12 @@ import { deriveQueryStatus } from '../utils/queryUtils';
|
|||||||
const placeholder: CustomFields = {};
|
const placeholder: CustomFields = {};
|
||||||
|
|
||||||
export default function useCustomFields() {
|
export default function useCustomFields() {
|
||||||
const { data, status, isFetching, isError, refetch } = useQuery({
|
const { data, status, isFetching, isError, isLoadingError, refetch } = useQuery({
|
||||||
queryKey: CUSTOM_FIELDS,
|
queryKey: CUSTOM_FIELDS,
|
||||||
queryFn: ({ signal }) => getCustomFields({ signal }),
|
queryFn: ({ signal }) => getCustomFields({ signal }),
|
||||||
placeholderData: (previousData, _previousQuery) => previousData,
|
placeholderData: (previousData, _previousQuery) => previousData,
|
||||||
refetchInterval: queryRefetchIntervalSlow,
|
refetchInterval: queryRefetchIntervalSlow,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: data ?? placeholder, status: deriveQueryStatus(status, data), isFetching, isError, refetch };
|
return { data: data ?? placeholder, status: deriveQueryStatus(status, isLoadingError), isFetching, isError, refetch };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,20 @@ import { projectDataPlaceholder } from '../models/ProjectData';
|
|||||||
import { deriveQueryStatus } from '../utils/queryUtils';
|
import { deriveQueryStatus } from '../utils/queryUtils';
|
||||||
|
|
||||||
export default function useProjectData() {
|
export default function useProjectData() {
|
||||||
const { data, status, isFetching, isError, refetch } = useQuery({
|
const { data, status, isFetching, isError, isLoadingError, refetch } = useQuery({
|
||||||
queryKey: PROJECT_DATA,
|
queryKey: PROJECT_DATA,
|
||||||
queryFn: ({ signal }) => getProjectData({ signal }),
|
queryFn: ({ signal }) => getProjectData({ signal }),
|
||||||
placeholderData: (previousData, _previousQuery) => previousData,
|
placeholderData: (previousData, _previousQuery) => previousData,
|
||||||
refetchInterval: queryRefetchIntervalSlow,
|
refetchInterval: queryRefetchIntervalSlow,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: data ?? projectDataPlaceholder, status: deriveQueryStatus(status, data), isFetching, isError, refetch };
|
return {
|
||||||
|
data: data ?? projectDataPlaceholder,
|
||||||
|
status: deriveQueryStatus(status, isLoadingError),
|
||||||
|
isFetching,
|
||||||
|
isError,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useUpdateProjectData() {
|
export function useUpdateProjectData() {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function useRundown() {
|
|||||||
data: { loaded: loadedRundownId },
|
data: { loaded: loadedRundownId },
|
||||||
} = useProjectRundowns();
|
} = useProjectRundowns();
|
||||||
|
|
||||||
const { data, status, isError, refetch, isFetching } = useQuery<Rundown>({
|
const { data, status, isError, isLoadingError, refetch, isFetching } = useQuery<Rundown>({
|
||||||
queryKey: loadedRundownId ? getRundownQueryKey(loadedRundownId) : CURRENT_RUNDOWN_QUERY_KEY,
|
queryKey: loadedRundownId ? getRundownQueryKey(loadedRundownId) : CURRENT_RUNDOWN_QUERY_KEY,
|
||||||
queryFn: ({ signal }) => fetchCurrentRundown({ signal }),
|
queryFn: ({ signal }) => fetchCurrentRundown({ signal }),
|
||||||
refetchInterval: queryRefetchIntervalSlow,
|
refetchInterval: queryRefetchIntervalSlow,
|
||||||
@@ -51,7 +51,13 @@ export default function useRundown() {
|
|||||||
queryClient.removeQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
queryClient.removeQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
||||||
}, [loadedRundownId, queryClient]);
|
}, [loadedRundownId, queryClient]);
|
||||||
|
|
||||||
return { data: data ?? cachedRundownPlaceholder, status: deriveQueryStatus(status, data), isError, refetch, isFetching };
|
return {
|
||||||
|
data: data ?? cachedRundownPlaceholder,
|
||||||
|
status: deriveQueryStatus(status, isLoadingError),
|
||||||
|
isError,
|
||||||
|
refetch,
|
||||||
|
isFetching,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRundownWithMetadata() {
|
export function useRundownWithMetadata() {
|
||||||
@@ -128,7 +134,7 @@ export function useRundownAuxData() {
|
|||||||
export function useRundownById(rundownId: string | null | undefined) {
|
export function useRundownById(rundownId: string | null | undefined) {
|
||||||
const enabled = Boolean(rundownId);
|
const enabled = Boolean(rundownId);
|
||||||
|
|
||||||
const { data, status, isError, refetch, isFetching } = useQuery<Rundown>({
|
const { data, status, isError, isLoadingError, refetch, isFetching } = useQuery<Rundown>({
|
||||||
queryKey: getRundownQueryKey(rundownId ?? ''),
|
queryKey: getRundownQueryKey(rundownId ?? ''),
|
||||||
queryFn: ({ signal }) => fetchRundown(rundownId!, { signal }),
|
queryFn: ({ signal }) => fetchRundown(rundownId!, { signal }),
|
||||||
enabled,
|
enabled,
|
||||||
@@ -136,5 +142,11 @@ export function useRundownById(rundownId: string | null | undefined) {
|
|||||||
refetchInterval: queryRefetchIntervalSlow,
|
refetchInterval: queryRefetchIntervalSlow,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: data ?? cachedRundownPlaceholder, status: deriveQueryStatus(status, data), isError, refetch, isFetching };
|
return {
|
||||||
|
data: data ?? cachedRundownPlaceholder,
|
||||||
|
status: deriveQueryStatus(status, isLoadingError),
|
||||||
|
isError,
|
||||||
|
refetch,
|
||||||
|
isFetching,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ontimePlaceholderSettings } from '../models/OntimeSettings';
|
|||||||
import { deriveQueryStatus } from '../utils/queryUtils';
|
import { deriveQueryStatus } from '../utils/queryUtils';
|
||||||
|
|
||||||
export default function useSettings() {
|
export default function useSettings() {
|
||||||
const { data, status, isFetching, isError, refetch } = useQuery({
|
const { data, status, isFetching, isError, isLoadingError, refetch } = useQuery({
|
||||||
queryKey: APP_SETTINGS,
|
queryKey: APP_SETTINGS,
|
||||||
queryFn: ({ signal }) => getSettings({ signal }),
|
queryFn: ({ signal }) => getSettings({ signal }),
|
||||||
placeholderData: (previousData, _previousQuery) => previousData,
|
placeholderData: (previousData, _previousQuery) => previousData,
|
||||||
@@ -23,5 +23,11 @@ export default function useSettings() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: data ?? ontimePlaceholderSettings, status: deriveQueryStatus(status, data), isFetching, isError, refetch };
|
return {
|
||||||
|
data: data ?? ontimePlaceholderSettings,
|
||||||
|
status: deriveQueryStatus(status, isLoadingError),
|
||||||
|
isFetching,
|
||||||
|
isError,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { viewsSettingsPlaceholder } from '../models/ViewSettings.type';
|
|||||||
import { deriveQueryStatus } from '../utils/queryUtils';
|
import { deriveQueryStatus } from '../utils/queryUtils';
|
||||||
|
|
||||||
export default function useViewSettings() {
|
export default function useViewSettings() {
|
||||||
const { data, status } = useQuery({
|
const { data, status, isLoadingError } = useQuery({
|
||||||
queryKey: VIEW_SETTINGS,
|
queryKey: VIEW_SETTINGS,
|
||||||
queryFn: ({ signal }) => getViewSettings({ signal }),
|
queryFn: ({ signal }) => getViewSettings({ signal }),
|
||||||
placeholderData: (previousData, _previousQuery) => previousData,
|
placeholderData: (previousData, _previousQuery) => previousData,
|
||||||
@@ -25,5 +25,5 @@ export default function useViewSettings() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: data ?? viewsSettingsPlaceholder, status: deriveQueryStatus(status, data), mutateAsync };
|
return { data: data ?? viewsSettingsPlaceholder, status: deriveQueryStatus(status, isLoadingError), mutateAsync };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import { deriveQueryStatus } from '../queryUtils';
|
import { deriveQueryStatus } from '../queryUtils';
|
||||||
|
|
||||||
test('keeps pending and success statuses unchanged', () => {
|
test('keeps pending and success statuses unchanged', () => {
|
||||||
expect(deriveQueryStatus('pending', undefined)).toBe('pending');
|
expect(deriveQueryStatus('pending', false)).toBe('pending');
|
||||||
expect(deriveQueryStatus('success', { some: 'data' })).toBe('success');
|
expect(deriveQueryStatus('success', false)).toBe('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('keeps error status when there is no data', () => {
|
test('keeps error status on a genuine loading error (no data ever received)', () => {
|
||||||
expect(deriveQueryStatus('error', undefined)).toBe('error');
|
expect(deriveQueryStatus('error', true)).toBe('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('downgrades error to success when data is still available', () => {
|
test('downgrades error to success on a refetch error (data still available)', () => {
|
||||||
expect(deriveQueryStatus('error', { some: 'data' })).toBe('success');
|
expect(deriveQueryStatus('error', false)).toBe('success');
|
||||||
expect(deriveQueryStatus('error', [])).toBe('success');
|
|
||||||
expect(deriveQueryStatus('error', 0)).toBe('success');
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import type { QueryStatus } from '@tanstack/react-query';
|
import type { QueryStatus } from '@tanstack/react-query';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A background refetch failure still leaves the last successfully fetched data in place.
|
* A background refetch failure (react-query's isRefetchError) still leaves the last
|
||||||
* In that case we want callers to keep treating the query as usable rather than erroring out,
|
* successfully fetched data in place, so we want callers to keep treating the query as
|
||||||
* so we only report 'error' when we have never received data for this query.
|
* usable rather than erroring out. Only a genuine isLoadingError (never received data)
|
||||||
|
* should surface as 'error'.
|
||||||
*/
|
*/
|
||||||
export function deriveQueryStatus(status: QueryStatus, data: unknown): QueryStatus {
|
export function deriveQueryStatus(status: QueryStatus, isLoadingError: boolean): QueryStatus {
|
||||||
if (status === 'error' && data !== undefined) {
|
if (status === 'error' && !isLoadingError) {
|
||||||
return 'success';
|
return 'success';
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
Reference in New Issue
Block a user