mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-14 13:57:29 -04:00
fix: Infinite API request loop on empty stores (#7613)
This commit is contained in:
@@ -13,9 +13,10 @@ describe("useStoreActions", () => {
|
||||
|
||||
const mockStore = ref([]);
|
||||
const mockLoading = ref(false);
|
||||
const mockInitialized = ref(false);
|
||||
|
||||
test("deleteMany calls deleteOne for each ID and refreshes once", async () => {
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
|
||||
|
||||
mockApi.deleteOne = vi.fn().mockResolvedValue({ response: { data: {} } });
|
||||
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
|
||||
@@ -32,7 +33,7 @@ describe("useStoreActions", () => {
|
||||
});
|
||||
|
||||
test("deleteMany handles empty array", async () => {
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
|
||||
|
||||
mockApi.deleteOne = vi.fn();
|
||||
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
|
||||
@@ -44,7 +45,7 @@ describe("useStoreActions", () => {
|
||||
});
|
||||
|
||||
test("deleteMany sets loading state", async () => {
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
|
||||
|
||||
mockApi.deleteOne = vi.fn().mockResolvedValue({});
|
||||
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
|
||||
@@ -55,4 +56,25 @@ describe("useStoreActions", () => {
|
||||
await promise;
|
||||
expect(mockLoading.value).toBe(false);
|
||||
});
|
||||
|
||||
test("refresh sets initialized to true even when store returns empty results", async () => {
|
||||
const localInitialized = ref(false);
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, localInitialized);
|
||||
|
||||
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
|
||||
|
||||
expect(localInitialized.value).toBe(false);
|
||||
await actions.refresh();
|
||||
expect(localInitialized.value).toBe(true);
|
||||
});
|
||||
|
||||
test("refresh sets initialized to true when store returns items", async () => {
|
||||
const localInitialized = ref(false);
|
||||
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, localInitialized);
|
||||
|
||||
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [{ id: "1", name: "item" }] } });
|
||||
|
||||
await actions.refresh();
|
||||
expect(localInitialized.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ export function useReadOnlyActions<T extends BoundT>(
|
||||
api: BaseCRUDAPIReadOnly<T>,
|
||||
allRef: Ref<T[] | null> | null,
|
||||
loading: Ref<boolean>,
|
||||
initialized: Ref<boolean>,
|
||||
defaultQueryParams: Record<string, QueryValue> = {},
|
||||
): ReadOnlyStoreActions<T> {
|
||||
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
@@ -69,6 +70,7 @@ export function useReadOnlyActions<T extends BoundT>(
|
||||
allRef.value = data.items;
|
||||
}
|
||||
|
||||
initialized.value = true;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -89,6 +91,7 @@ export function useStoreActions<T extends BoundT>(
|
||||
api: BaseCRUDAPI<unknown, T, unknown>,
|
||||
allRef: Ref<T[] | null> | null,
|
||||
loading: Ref<boolean>,
|
||||
initialized: Ref<boolean>,
|
||||
defaultQueryParams: Record<string, QueryValue> = {},
|
||||
): StoreActions<T> {
|
||||
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
@@ -132,6 +135,7 @@ export function useStoreActions<T extends BoundT>(
|
||||
allRef.value = data.items;
|
||||
}
|
||||
|
||||
initialized.value = true;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ export const useReadOnlyStore = function <T extends BoundT>(
|
||||
storeKey: string,
|
||||
store: Ref<T[]>,
|
||||
loading: Ref<boolean>,
|
||||
initialized: Ref<boolean>,
|
||||
api: BaseCRUDAPIReadOnly<T>,
|
||||
params = {} as Record<string, QueryValue>,
|
||||
) {
|
||||
const storeActions = useReadOnlyActions(`${storeKey}-store-readonly`, api, store, loading);
|
||||
const storeActions = useReadOnlyActions(`${storeKey}-store-readonly`, api, store, loading, initialized);
|
||||
const actions = {
|
||||
...storeActions,
|
||||
async refresh() {
|
||||
@@ -27,11 +28,12 @@ export const useReadOnlyStore = function <T extends BoundT>(
|
||||
},
|
||||
flushStore() {
|
||||
store.value = [];
|
||||
initialized.value = false;
|
||||
},
|
||||
};
|
||||
|
||||
// initial hydration
|
||||
if (!loading.value && !store.value.length) {
|
||||
if (!loading.value && !initialized.value) {
|
||||
actions.refresh();
|
||||
}
|
||||
|
||||
@@ -42,10 +44,11 @@ export const useStore = function <T extends BoundT>(
|
||||
storeKey: string,
|
||||
store: Ref<T[]>,
|
||||
loading: Ref<boolean>,
|
||||
initialized: Ref<boolean>,
|
||||
api: BaseCRUDAPI<unknown, T, unknown>,
|
||||
params = {} as Record<string, QueryValue>,
|
||||
) {
|
||||
const storeActions = useStoreActions(`${storeKey}-store`, api, store, loading);
|
||||
const storeActions = useStoreActions(`${storeKey}-store`, api, store, loading, initialized);
|
||||
const actions = {
|
||||
...storeActions,
|
||||
async refresh() {
|
||||
@@ -53,11 +56,12 @@ export const useStore = function <T extends BoundT>(
|
||||
},
|
||||
flushStore() {
|
||||
store.value = [];
|
||||
initialized.value = false;
|
||||
},
|
||||
};
|
||||
|
||||
// initial hydration
|
||||
if (!loading.value && !store.value.length) {
|
||||
if (!loading.value && !initialized.value) {
|
||||
actions.refresh();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user