mirror of
https://github.com/directus/directus.git
synced 2026-02-06 09:45:06 -05:00
Fix use-items loading state when an existing request gets canceled (#16881)
* prevent canceled requests from clearing loadingTimeout * rename CancelTokenSource variable * don't set loading to false if there's still loadingTimeout * updated request cancellation to use the abort controller * azri's getItemCount fix * prevent cancelled requests from throwing errors * prevent count request from firing twice * Remove fetchOnInit option Co-authored-by: Brainslug <tim@brainslug.nl> Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -111,8 +111,7 @@ export default defineLayout<LayoutOptions>({
|
||||
fields: queryFields,
|
||||
filter: filterWithCalendarView,
|
||||
search: search,
|
||||
},
|
||||
false
|
||||
}
|
||||
);
|
||||
|
||||
const events: Ref<EventInput> = computed(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useApi } from './use-system';
|
||||
import axios, { CancelTokenSource } from 'axios';
|
||||
import axios from 'axios';
|
||||
import { useCollection } from './use-collection';
|
||||
import { Item, Query } from '../types';
|
||||
import { moveInArray } from '../utils';
|
||||
@@ -34,7 +34,7 @@ type ComputedQuery = {
|
||||
page: Ref<Query['page']> | WritableComputedRef<Query['page']>;
|
||||
};
|
||||
|
||||
export function useItems(collection: Ref<string | null>, query: ComputedQuery, fetchOnInit = true): UsableItems {
|
||||
export function useItems(collection: Ref<string | null>, query: ComputedQuery): UsableItems {
|
||||
const api = useApi();
|
||||
const { primaryKeyField } = useCollection(collection);
|
||||
|
||||
@@ -60,15 +60,15 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
return Math.ceil(itemCount.value / (unref(limit) ?? 100));
|
||||
});
|
||||
|
||||
let currentRequest: CancelTokenSource | null = null;
|
||||
const existingRequests: Record<'items' | 'total' | 'filter', AbortController | null> = {
|
||||
items: null,
|
||||
total: null,
|
||||
filter: null,
|
||||
};
|
||||
let loadingTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
const fetchItems = throttle(getItems, 500);
|
||||
|
||||
if (fetchOnInit) {
|
||||
fetchItems();
|
||||
}
|
||||
|
||||
watch(
|
||||
[collection, limit, sort, search, filter, fields, page],
|
||||
async (after, before) => {
|
||||
@@ -79,6 +79,10 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
|
||||
if (!newCollection || !query) return;
|
||||
|
||||
if (newCollection !== oldCollection) {
|
||||
reset();
|
||||
}
|
||||
|
||||
if (
|
||||
!isEqual(newFilter, oldFilter) ||
|
||||
!isEqual(newSort, oldSort) ||
|
||||
@@ -90,14 +94,10 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEqual(newFilter, oldFilter) || newSearch !== oldSearch) {
|
||||
if (newCollection !== oldCollection || !isEqual(newFilter, oldFilter) || newSearch !== oldSearch) {
|
||||
getItemCount();
|
||||
}
|
||||
|
||||
if (newCollection !== oldCollection) {
|
||||
reset();
|
||||
}
|
||||
|
||||
fetchItems();
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
@@ -119,8 +119,10 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
async function getItems() {
|
||||
if (!endpoint.value) return;
|
||||
|
||||
currentRequest?.cancel();
|
||||
currentRequest = null;
|
||||
let isCurrentRequestCanceled = false;
|
||||
|
||||
if (existingRequests.items) existingRequests.items.abort();
|
||||
existingRequests.items = new AbortController();
|
||||
|
||||
error.value = null;
|
||||
|
||||
@@ -152,8 +154,6 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
fieldsToFetch = fieldsToFetch.filter((field) => field.startsWith('$') === false);
|
||||
|
||||
try {
|
||||
currentRequest = axios.CancelToken.source();
|
||||
|
||||
const response = await api.get<any>(endpoint.value, {
|
||||
params: {
|
||||
limit: unref(limit),
|
||||
@@ -164,10 +164,11 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
search: unref(search),
|
||||
filter: unref(filter),
|
||||
},
|
||||
cancelToken: currentRequest.token,
|
||||
signal: existingRequests.items.signal,
|
||||
});
|
||||
|
||||
let fetchedItems = response.data.data;
|
||||
existingRequests.items = null;
|
||||
|
||||
/**
|
||||
* @NOTE
|
||||
@@ -192,16 +193,18 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
page.value = 1;
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (!axios.isCancel(err)) {
|
||||
if (axios.isCancel(err)) {
|
||||
isCurrentRequestCanceled = true;
|
||||
} else {
|
||||
error.value = err;
|
||||
}
|
||||
} finally {
|
||||
if (loadingTimeout) {
|
||||
if (loadingTimeout && !isCurrentRequestCanceled) {
|
||||
clearTimeout(loadingTimeout);
|
||||
loadingTimeout = null;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
if (!loadingTimeout) loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,34 +230,56 @@ export function useItems(collection: Ref<string | null>, query: ComputedQuery, f
|
||||
async function getTotalCount() {
|
||||
if (!endpoint.value) return;
|
||||
|
||||
const response = await api.get<any>(endpoint.value, {
|
||||
params: {
|
||||
aggregate: {
|
||||
count: '*',
|
||||
try {
|
||||
if (existingRequests.total) existingRequests.total.abort();
|
||||
existingRequests.total = new AbortController();
|
||||
|
||||
const response = await api.get<any>(endpoint.value, {
|
||||
params: {
|
||||
aggregate: {
|
||||
count: '*',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
signal: existingRequests.total.signal,
|
||||
});
|
||||
|
||||
const count = Number(response.data.data[0].count);
|
||||
const count = Number(response.data.data[0].count);
|
||||
existingRequests.total = null;
|
||||
|
||||
totalCount.value = count;
|
||||
totalCount.value = count;
|
||||
} catch (err: any) {
|
||||
if (!axios.isCancel(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getItemCount() {
|
||||
if (!endpoint.value) return;
|
||||
|
||||
const response = await api.get<any>(endpoint.value, {
|
||||
params: {
|
||||
filter: unref(filter),
|
||||
search: unref(search),
|
||||
aggregate: {
|
||||
count: '*',
|
||||
try {
|
||||
if (existingRequests.filter) existingRequests.filter.abort();
|
||||
existingRequests.filter = new AbortController();
|
||||
|
||||
const response = await api.get<any>(endpoint.value, {
|
||||
params: {
|
||||
filter: unref(filter),
|
||||
search: unref(search),
|
||||
aggregate: {
|
||||
count: '*',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
signal: existingRequests.filter.signal,
|
||||
});
|
||||
|
||||
const count = Number(response.data.data[0].count);
|
||||
const count = Number(response.data.data[0].count);
|
||||
existingRequests.filter = null;
|
||||
|
||||
itemCount.value = count;
|
||||
itemCount.value = count;
|
||||
} catch (err: any) {
|
||||
if (!axios.isCancel(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user