Skip to content

Commit 6099cc0

Browse files
committed
feat: dynamic lazy + handle race condition
1 parent 5117cbb commit 6099cc0

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

src/components/createUseList.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@ export default function createUseList<Models, IdType>(
7272
options.include ? unwrap(options.include) : []
7373
);
7474
const path = computed(() => (options.path ? unwrap(options.path) : null));
75+
const lazy = computed(() =>
76+
options.lazy !== undefined ? unwrap(options.lazy) : false
77+
);
7578

76-
const autoFetch = computed(() => !options.lazy);
79+
const autoFetch = computed(() => !lazy.value);
7780

7881
const { error, state, hasLoaded, isLoading, isFailed, handleError } =
7982
useFetchState(!!options.loadOnUpdate);
@@ -90,6 +93,8 @@ export default function createUseList<Models, IdType>(
9093
});
9194
}
9295

96+
const requestKey = Math.random() * performance.now();
97+
9398
/**
9499
* Fetch list
95100
*/
@@ -101,7 +106,8 @@ export default function createUseList<Models, IdType>(
101106
pagination.value,
102107
sort.value,
103108
include.value,
104-
path.value
109+
path.value,
110+
requestKey
105111
)
106112
.then((res: any) => {
107113
const paginationChanged =
@@ -138,6 +144,10 @@ export default function createUseList<Models, IdType>(
138144
if (autoFetch.value) fetch();
139145
});
140146

147+
watch(autoFetch, () => {
148+
if (autoFetch.value) fetch();
149+
});
150+
141151
/**
142152
* Load the items
143153
*/

src/createStores.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ function createStore(
130130
return defineStore('vroom:' + name, {
131131
state: () => ({
132132
items: [] as any[],
133+
requestKeys: {} as { [key: string]: number },
133134
}),
134135
getters: {
135136
list(state) {
@@ -159,7 +160,8 @@ function createStore(
159160
pagination: PaginationSettings,
160161
sort: SortSettings[],
161162
include: string[],
162-
overridePath: string | null
163+
overridePath: string | null,
164+
requestKey?: string
163165
) {
164166
let params = {
165167
...parseFilters(filter),
@@ -172,7 +174,16 @@ function createStore(
172174
}
173175
const url = overridePath || endpoint;
174176

177+
const startedAt = performance.now();
178+
if (requestKey) {
179+
this.requestKeys[requestKey] = startedAt;
180+
}
181+
175182
return api.get(url, params).then((res: any) => {
183+
if (requestKey) {
184+
if (this.requestKeys[requestKey] !== startedAt) return;
185+
delete this.requestKeys[requestKey];
186+
}
176187
if (settings.envelope === false) {
177188
this.add(res);
178189
return { items: res };

test/helpers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export async function wait(delay = 0) {
2+
await new Promise((r) => setTimeout(r, delay));
3+
}

test/uselist.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createVroom, defineModel } from '.';
33
import { beforeEach, describe, it, expect, vi, test } from 'vitest';
44
import { createApp, defineComponent, nextTick, ref } from 'vue';
55
import { createPinia } from 'pinia';
6+
import { wait } from './helpers';
67

78
const app = createApp({});
89
const vroom = createVroom({
@@ -241,4 +242,47 @@ describe('Use list', () => {
241242
expect(wrapper.vm.isLoading).toBe(false);
242243
expect(wrapper.vm.items).toHaveLength(4);
243244
});
245+
246+
it('Lazy load is dynamic', async () => {
247+
const lazy = ref(true);
248+
const wrapper = getWrapper('book', { lazy });
249+
250+
expect(wrapper.vm.isLoading).toBe(false);
251+
252+
lazy.value = false;
253+
await wait();
254+
expect(wrapper.vm.isLoading).toBe(true);
255+
await wait();
256+
expect(wrapper.vm.isLoading).toBe(false);
257+
expect(wrapper.vm.items).toHaveLength(4);
258+
});
259+
260+
it('Handles race condition', async () => {
261+
const mock = {
262+
async sleep(delay: number) {
263+
await wait(delay);
264+
return;
265+
},
266+
};
267+
vroom.server?.get('/books', async (request, db) => {
268+
const { delay } = request.query as any;
269+
const asNumber = parseInt(delay);
270+
await mock.sleep(asNumber);
271+
return { data: db.book.all(), meta: { delay: asNumber } };
272+
});
273+
const spy = vi.spyOn(mock, 'sleep');
274+
275+
const delay = ref(2);
276+
277+
const wrapper = getWrapper('book', { filter: { delay } });
278+
await wait();
279+
delay.value = 10;
280+
await wait();
281+
delay.value = 5;
282+
await wait(20); // wait for everything to complete
283+
expect(wrapper.vm.items).toHaveLength(4);
284+
expect(spy).toHaveBeenCalledTimes(3);
285+
// @ts-ignore
286+
expect(wrapper.vm.meta.delay).toBe(5);
287+
});
244288
});

0 commit comments

Comments
 (0)