Skip to content

Commit 7c34351

Browse files
committed
feat(useLoadMore): refactor refresh and cancel of useLoadMore #36
1 parent 66b3198 commit 7c34351

File tree

2 files changed

+336
-9
lines changed

2 files changed

+336
-9
lines changed

src/__tests__/load-more.test.tsx

+284
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,290 @@ describe('useLoadMore', () => {
429429
}
430430
});
431431

432+
test('refresh should work, case: 1', async () => {
433+
const wrapper = shallowMount(
434+
defineComponent({
435+
setup() {
436+
const {
437+
dataList,
438+
loadingMore,
439+
loading,
440+
loadMore,
441+
refresh,
442+
refreshing,
443+
} = useLoadMore(normalRequest);
444+
return () => (
445+
<div>
446+
<div class="dataList">{dataList.value.length || 0}</div>
447+
<div class="loadingMore">{`${loadingMore.value}`}</div>
448+
<div class="loading">{`${loading.value}`}</div>
449+
<div class="refreshing">{`${refreshing.value}`}</div>
450+
<div
451+
class="loadMore"
452+
onClick={() => {
453+
loadMore();
454+
}}
455+
/>
456+
<div
457+
class="refresh"
458+
onClick={() => {
459+
refresh();
460+
}}
461+
/>
462+
</div>
463+
);
464+
},
465+
}),
466+
);
467+
468+
const dataListEl = wrapper.find('.dataList');
469+
const loadingMoreEl = wrapper.find('.loadingMore');
470+
const loadingEl = wrapper.find('.loading');
471+
const loadMoreEl = wrapper.find('.loadMore');
472+
const refreshEl = wrapper.find('.refresh');
473+
const refreshingEl = wrapper.find('.refreshing');
474+
475+
expect(loadingEl.text()).toBe('true');
476+
expect(refreshingEl.text()).toBe('false');
477+
await waitForTime(1000);
478+
expect(refreshingEl.text()).toBe('false');
479+
expect(loadingEl.text()).toBe('false');
480+
expect(dataListEl.text()).toBe('10');
481+
expect(loadingMoreEl.text()).toBe('false');
482+
483+
for (let index = 1; index <= 5; index++) {
484+
await loadMoreEl.trigger('click');
485+
expect(loadingMoreEl.text()).toBe('true');
486+
expect(loadingEl.text()).toBe('true');
487+
expect(refreshingEl.text()).toBe('false');
488+
await waitForTime(1000);
489+
expect(loadingEl.text()).toBe('false');
490+
expect(loadingMoreEl.text()).toBe('false');
491+
expect(refreshingEl.text()).toBe('false');
492+
expect(dataListEl.text()).toBe(`${10 + index * 10}`);
493+
}
494+
495+
expect(dataListEl.text()).toBe('60');
496+
expect(loadingMoreEl.text()).toBe('false');
497+
expect(loadingEl.text()).toBe('false');
498+
expect(refreshingEl.text()).toBe('false');
499+
500+
await refreshEl.trigger('click');
501+
expect(dataListEl.text()).toBe('60');
502+
expect(loadingMoreEl.text()).toBe('false');
503+
expect(loadingEl.text()).toBe('true');
504+
expect(refreshingEl.text()).toBe('true');
505+
506+
await waitForTime(1000);
507+
expect(dataListEl.text()).toBe('10');
508+
expect(loadingMoreEl.text()).toBe('false');
509+
expect(loadingEl.text()).toBe('false');
510+
expect(refreshingEl.text()).toBe('false');
511+
});
512+
513+
test('refresh should work, case: 2', async () => {
514+
const wrapper = shallowMount(
515+
defineComponent({
516+
setup() {
517+
const {
518+
dataList,
519+
loadingMore,
520+
loading,
521+
refresh,
522+
refreshing,
523+
} = useLoadMore(normalRequest);
524+
return () => (
525+
<div>
526+
<div class="dataList">{dataList.value.length || 0}</div>
527+
<div class="loadingMore">{`${loadingMore.value}`}</div>
528+
<div class="loading">{`${loading.value}`}</div>
529+
<div class="refreshing">{`${refreshing.value}`}</div>
530+
<div
531+
class="refresh"
532+
onClick={() => {
533+
refresh();
534+
}}
535+
/>
536+
</div>
537+
);
538+
},
539+
}),
540+
);
541+
542+
const dataListEl = wrapper.find('.dataList');
543+
const loadingMoreEl = wrapper.find('.loadingMore');
544+
const loadingEl = wrapper.find('.loading');
545+
const refreshEl = wrapper.find('.refresh');
546+
const refreshingEl = wrapper.find('.refreshing');
547+
548+
expect(loadingEl.text()).toBe('true');
549+
expect(refreshingEl.text()).toBe('false');
550+
expect(loadingMoreEl.text()).toBe('false');
551+
await refreshEl.trigger('click');
552+
expect(loadingEl.text()).toBe('true');
553+
expect(refreshingEl.text()).toBe('true');
554+
expect(loadingMoreEl.text()).toBe('false');
555+
await waitForTime(1000);
556+
expect(dataListEl.text()).toBe('10');
557+
});
558+
559+
test('cancel should work, case: 1', async () => {
560+
const wrapper = shallowMount(
561+
defineComponent({
562+
setup() {
563+
const {
564+
dataList,
565+
loadingMore,
566+
loading,
567+
loadMore,
568+
refresh,
569+
refreshing,
570+
cancel,
571+
reload,
572+
} = useLoadMore(normalRequest);
573+
return () => (
574+
<div>
575+
<div class="dataList">{dataList.value.length || 0}</div>
576+
<div class="loadingMore">{`${loadingMore.value}`}</div>
577+
<div class="loading">{`${loading.value}`}</div>
578+
<div class="refreshing">{`${refreshing.value}`}</div>
579+
<div
580+
class="loadMore"
581+
onClick={() => {
582+
loadMore();
583+
}}
584+
/>
585+
<div
586+
class="refresh"
587+
onClick={() => {
588+
refresh();
589+
}}
590+
/>
591+
<div
592+
class="cancel"
593+
onClick={() => {
594+
cancel();
595+
}}
596+
/>
597+
<div
598+
class="reload"
599+
onClick={() => {
600+
reload();
601+
}}
602+
/>
603+
</div>
604+
);
605+
},
606+
}),
607+
);
608+
609+
const dataListEl = wrapper.find('.dataList');
610+
const loadingMoreEl = wrapper.find('.loadingMore');
611+
const loadingEl = wrapper.find('.loading');
612+
const loadMoreEl = wrapper.find('.loadMore');
613+
const refreshEl = wrapper.find('.refresh');
614+
const refreshingEl = wrapper.find('.refreshing');
615+
const cancelEl = wrapper.find('.cancel');
616+
const reloadEl = wrapper.find('.reload');
617+
618+
expect(loadingEl.text()).toBe('true');
619+
expect(refreshingEl.text()).toBe('false');
620+
expect(loadingMoreEl.text()).toBe('false');
621+
await waitForTime(1000);
622+
expect(loadingEl.text()).toBe('false');
623+
expect(dataListEl.text()).toBe('10');
624+
expect(loadingMoreEl.text()).toBe('false');
625+
626+
// trigger loadMore
627+
await loadMoreEl.trigger('click');
628+
expect(loadingEl.text()).toBe('true');
629+
expect(loadingMoreEl.text()).toBe('true');
630+
expect(refreshingEl.text()).toBe('false');
631+
await waitForTime(100);
632+
// trigger cancel
633+
await cancelEl.trigger('click');
634+
expect(loadingEl.text()).toBe('false');
635+
expect(loadingMoreEl.text()).toBe('false');
636+
expect(refreshingEl.text()).toBe('false');
637+
expect(dataListEl.text()).toBe('10');
638+
639+
// trigger refresh
640+
await refreshEl.trigger('click');
641+
expect(loadingEl.text()).toBe('true');
642+
expect(loadingMoreEl.text()).toBe('false');
643+
expect(refreshingEl.text()).toBe('true');
644+
await waitForTime(100);
645+
// trigger cancel
646+
await cancelEl.trigger('click');
647+
expect(loadingEl.text()).toBe('false');
648+
expect(loadingMoreEl.text()).toBe('false');
649+
expect(refreshingEl.text()).toBe('false');
650+
expect(dataListEl.text()).toBe('10');
651+
652+
// trigger reload
653+
await reloadEl.trigger('click');
654+
expect(loadingEl.text()).toBe('true');
655+
expect(loadingMoreEl.text()).toBe('false');
656+
expect(refreshingEl.text()).toBe('false');
657+
await waitForTime(100);
658+
// trigger cancel
659+
await cancelEl.trigger('click');
660+
expect(loadingEl.text()).toBe('false');
661+
expect(loadingMoreEl.text()).toBe('false');
662+
expect(refreshingEl.text()).toBe('false');
663+
expect(dataListEl.text()).toBe('0');
664+
});
665+
666+
test('cancel should work, case: 2', async () => {
667+
const wrapper = shallowMount(
668+
defineComponent({
669+
setup() {
670+
const {
671+
dataList,
672+
loadingMore,
673+
loading,
674+
refreshing,
675+
cancel,
676+
} = useLoadMore(normalRequest);
677+
return () => (
678+
<div>
679+
<div class="dataList">{dataList.value.length || 0}</div>
680+
<div class="loadingMore">{`${loadingMore.value}`}</div>
681+
<div class="loading">{`${loading.value}`}</div>
682+
<div class="refreshing">{`${refreshing.value}`}</div>
683+
<div
684+
class="cancel"
685+
onClick={() => {
686+
cancel();
687+
}}
688+
/>
689+
</div>
690+
);
691+
},
692+
}),
693+
);
694+
695+
const dataListEl = wrapper.find('.dataList');
696+
const loadingMoreEl = wrapper.find('.loadingMore');
697+
const loadingEl = wrapper.find('.loading');
698+
const refreshingEl = wrapper.find('.refreshing');
699+
const cancelEl = wrapper.find('.cancel');
700+
701+
expect(loadingEl.text()).toBe('true');
702+
expect(refreshingEl.text()).toBe('false');
703+
expect(loadingMoreEl.text()).toBe('false');
704+
705+
// trigger cancel
706+
await cancelEl.trigger('click');
707+
expect(loadingEl.text()).toBe('false');
708+
expect(refreshingEl.text()).toBe('false');
709+
expect(loadingMoreEl.text()).toBe('false');
710+
expect(dataListEl.text()).toBe('0');
711+
712+
await waitForTime(1000);
713+
expect(dataListEl.text()).toBe('0');
714+
});
715+
432716
test('useLoadMore when request error', async () => {
433717
const wrapper = shallowMount(
434718
defineComponent({

src/useLoadMore.ts

+52-9
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ import get from 'lodash/get';
1212
import generateService from './core/utils/generateService';
1313
import { isFunction } from './core/utils';
1414
import { ServiceParams } from './core/utils/types';
15+
import omit from 'lodash/omit';
1516

1617
export interface LoadMoreResult<R, P extends unknown[], LR extends unknown[]>
17-
extends Omit<BaseResult<R, P>, 'queries'> {
18+
extends Omit<BaseResult<R, P>, 'queries' | 'refresh'> {
1819
dataList: Ref<LR>;
1920
noMore: Ref<boolean>;
2021
loadingMore: Ref<boolean>;
22+
refreshing: Ref<boolean>;
2123
loadMore: () => void;
2224
reload: () => void;
25+
refresh: () => void;
2326
}
2427

2528
export type LoadMoreExtendsOption = {
@@ -104,13 +107,19 @@ function useLoadMore<R, P extends unknown[], FR, LR extends unknown[]>(
104107
throw new Error('useLoadMore does not support concurrent request');
105108
}
106109

110+
const refreshing = ref(false);
107111
const loadingMore = ref(false);
108-
const increaseQueryKey = ref(0);
109-
const { data, params, queries, run, reset, ...rest } = useAsyncQuery<
110-
R,
111-
P,
112-
FR
113-
>(promiseQuery, {
112+
const initailIncreaseQueryKey = 0;
113+
const increaseQueryKey = ref(initailIncreaseQueryKey);
114+
const {
115+
data,
116+
params,
117+
queries,
118+
run,
119+
reset,
120+
cancel: _cancel,
121+
...rest
122+
} = useAsyncQuery<R, P, FR>(promiseQuery, {
114123
...restOptions,
115124
onSuccess: (...p) => {
116125
loadingMore.value = false;
@@ -161,26 +170,60 @@ function useLoadMore<R, P extends unknown[], FR, LR extends unknown[]>(
161170
run(...mergerParams);
162171
};
163172

173+
const unmountQueries = () => {
174+
Object.keys(queries).forEach(key => {
175+
if (key !== initailIncreaseQueryKey.toString()) {
176+
queries[key].cancel();
177+
queries[key].unmount();
178+
delete queries[key];
179+
}
180+
});
181+
};
182+
183+
const refresh = async () => {
184+
refreshing.value = true;
185+
const latestKey = increaseQueryKey.value - 1;
186+
const key =
187+
latestKey < initailIncreaseQueryKey ? initailIncreaseQueryKey : latestKey;
188+
189+
latestData.value = queries[key].data;
190+
increaseQueryKey.value = initailIncreaseQueryKey;
191+
const [, ...restParams] = params.value;
192+
const mergerParams = [undefined, ...restParams] as any;
193+
await run(...mergerParams);
194+
unmountQueries();
195+
refreshing.value = false;
196+
};
197+
164198
const reload = () => {
165199
reset();
166-
increaseQueryKey.value = 0;
200+
increaseQueryKey.value = initailIncreaseQueryKey;
167201
latestData.value = undefined;
168202
const [, ...restParams] = params.value;
169203
const mergerParams = [undefined, ...restParams] as any;
170204
run(...mergerParams);
171205
};
172206

207+
const cancel = () => {
208+
_cancel();
209+
loadingMore.value = false;
210+
refreshing.value = false;
211+
};
212+
173213
return {
174214
data: latestData,
175215
dataList: dataList,
176216
params,
177217
noMore,
178218
loadingMore,
219+
refreshing,
179220
run,
180221
reload,
181222
loadMore,
182223
reset,
183-
...rest,
224+
refresh,
225+
cancel,
226+
...omit(rest, ['refresh']),
184227
};
185228
}
186229

0 commit comments

Comments
 (0)