Skip to content

Commit decbbd3

Browse files
committed
feat(core): add throttle and debounce options #63
1 parent da138f0 commit decbbd3

File tree

3 files changed

+255
-5
lines changed

3 files changed

+255
-5
lines changed

Diff for: src/__tests__/index.test.tsx

+241
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,134 @@ describe('useRequest', () => {
855855
expect(mockFn).toHaveBeenCalledTimes(2);
856856
});
857857

858+
test('debounceOptions should work: case 1', async () => {
859+
const mockFn = jest.fn();
860+
861+
const wrapper = shallowMount(
862+
defineComponent({
863+
setup() {
864+
const { run } = useRequest(
865+
() => {
866+
mockFn();
867+
return request();
868+
},
869+
{
870+
debounceInterval: 100,
871+
debounceOptions: {
872+
leading: true,
873+
trailing: false,
874+
},
875+
manual: true,
876+
},
877+
);
878+
return () => <button onClick={() => run()} />;
879+
},
880+
}),
881+
);
882+
for (let index = 0; index < 100; index++) {
883+
await wrapper.find('button').trigger('click');
884+
await waitForTime(50);
885+
}
886+
887+
expect(mockFn).toHaveBeenCalledTimes(1);
888+
await waitForTime(100);
889+
expect(mockFn).toHaveBeenCalledTimes(1);
890+
891+
for (let index = 0; index < 100; index++) {
892+
await wrapper.find('button').trigger('click');
893+
await waitForTime(50);
894+
}
895+
896+
expect(mockFn).toHaveBeenCalledTimes(2);
897+
await waitForTime(100);
898+
expect(mockFn).toHaveBeenCalledTimes(2);
899+
});
900+
901+
test('debounceOptions should work: case 2', async () => {
902+
const mockFn = jest.fn();
903+
904+
const wrapper = shallowMount(
905+
defineComponent({
906+
setup() {
907+
const { run } = useRequest(
908+
() => {
909+
mockFn();
910+
return request();
911+
},
912+
{
913+
debounceInterval: 100,
914+
debounceOptions: {
915+
leading: false,
916+
trailing: false,
917+
},
918+
manual: true,
919+
},
920+
);
921+
return () => <button onClick={() => run()} />;
922+
},
923+
}),
924+
);
925+
for (let index = 0; index < 100; index++) {
926+
await wrapper.find('button').trigger('click');
927+
await waitForTime(50);
928+
}
929+
930+
expect(mockFn).toHaveBeenCalledTimes(0);
931+
await waitForTime(100);
932+
expect(mockFn).toHaveBeenCalledTimes(0);
933+
934+
for (let index = 0; index < 100; index++) {
935+
await wrapper.find('button').trigger('click');
936+
await waitForTime(50);
937+
}
938+
939+
expect(mockFn).toHaveBeenCalledTimes(0);
940+
await waitForTime(100);
941+
expect(mockFn).toHaveBeenCalledTimes(0);
942+
});
943+
944+
test('debounceOptions should work: case 3', async () => {
945+
const mockFn = jest.fn();
946+
947+
const wrapper = shallowMount(
948+
defineComponent({
949+
setup() {
950+
const { run } = useRequest(
951+
() => {
952+
mockFn();
953+
return request();
954+
},
955+
{
956+
debounceInterval: 500,
957+
debounceOptions: {
958+
maxWait: 1000,
959+
},
960+
manual: true,
961+
},
962+
);
963+
return () => <button onClick={() => run()} />;
964+
},
965+
}),
966+
);
967+
for (let index = 0; index < 100; index++) {
968+
await wrapper.find('button').trigger('click');
969+
await waitForTime(50);
970+
}
971+
972+
expect(mockFn).toHaveBeenCalledTimes(5);
973+
await waitForTime(1000);
974+
expect(mockFn).toHaveBeenCalledTimes(5);
975+
976+
for (let index = 0; index < 100; index++) {
977+
await wrapper.find('button').trigger('click');
978+
await waitForTime(50);
979+
}
980+
981+
expect(mockFn).toHaveBeenCalledTimes(10);
982+
await waitForTime(1000);
983+
expect(mockFn).toHaveBeenCalledTimes(10);
984+
});
985+
858986
test('debounceInterval should work with cancel', async () => {
859987
const mockFn = jest.fn();
860988

@@ -964,6 +1092,119 @@ describe('useRequest', () => {
9641092
expect(mockFn).toHaveBeenCalledTimes(3);
9651093
});
9661094

1095+
test('throttleOptions should work, case: 1', async () => {
1096+
const mockFn = jest.fn();
1097+
1098+
const wrapper = shallowMount(
1099+
defineComponent({
1100+
setup() {
1101+
const { run } = useRequest(
1102+
() => {
1103+
mockFn();
1104+
return request();
1105+
},
1106+
{
1107+
throttleInterval: 100,
1108+
throttleOptions: {
1109+
leading: false,
1110+
},
1111+
manual: true,
1112+
},
1113+
);
1114+
return () => <button onClick={() => run()} />;
1115+
},
1116+
}),
1117+
);
1118+
1119+
await wrapper.find('button').trigger('click');
1120+
1121+
await waitForTime(50);
1122+
await wrapper.find('button').trigger('click');
1123+
1124+
await waitForTime(50);
1125+
await wrapper.find('button').trigger('click');
1126+
1127+
await waitForAll();
1128+
// have been call 2 times
1129+
// because the function will only invoking on the trailing edge of the timeout
1130+
expect(mockFn).toHaveBeenCalledTimes(2);
1131+
});
1132+
1133+
test('throttleOptions should work, case: 2', async () => {
1134+
const mockFn = jest.fn();
1135+
1136+
const wrapper = shallowMount(
1137+
defineComponent({
1138+
setup() {
1139+
const { run } = useRequest(
1140+
() => {
1141+
mockFn();
1142+
return request();
1143+
},
1144+
{
1145+
throttleInterval: 100,
1146+
throttleOptions: {
1147+
trailing: false,
1148+
},
1149+
manual: true,
1150+
},
1151+
);
1152+
return () => <button onClick={() => run()} />;
1153+
},
1154+
}),
1155+
);
1156+
1157+
await wrapper.find('button').trigger('click');
1158+
1159+
await waitForTime(50);
1160+
await wrapper.find('button').trigger('click');
1161+
1162+
await waitForTime(50);
1163+
await wrapper.find('button').trigger('click');
1164+
1165+
await waitForAll();
1166+
// have been call 2 times
1167+
// because the function will only invoking on the leading edge of the timeout
1168+
expect(mockFn).toHaveBeenCalledTimes(2);
1169+
});
1170+
1171+
test('throttleOptions should work, case: 3', async () => {
1172+
const mockFn = jest.fn();
1173+
1174+
const wrapper = shallowMount(
1175+
defineComponent({
1176+
setup() {
1177+
const { run } = useRequest(
1178+
() => {
1179+
mockFn();
1180+
return request();
1181+
},
1182+
{
1183+
throttleInterval: 100,
1184+
throttleOptions: {
1185+
leading: false,
1186+
trailing: false,
1187+
},
1188+
manual: true,
1189+
},
1190+
);
1191+
return () => <button onClick={() => run()} />;
1192+
},
1193+
}),
1194+
);
1195+
1196+
await wrapper.find('button').trigger('click');
1197+
1198+
await waitForTime(50);
1199+
await wrapper.find('button').trigger('click');
1200+
1201+
await waitForTime(50);
1202+
await wrapper.find('button').trigger('click');
1203+
1204+
await waitForAll();
1205+
expect(mockFn).toHaveBeenCalledTimes(0);
1206+
});
1207+
9671208
test('throttleInterval should work with cancel', async () => {
9681209
const mockFn = jest.fn();
9691210

Diff for: src/core/createQuery.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ const createQuery = <R, P extends unknown[]>(
3737
loadingDelay,
3838
pollingInterval,
3939
debounceInterval,
40+
debounceOptions,
4041
throttleInterval,
42+
throttleOptions,
4143
pollingWhenHidden,
4244
pollingWhenOffline,
4345
errorRetryCount,
@@ -222,13 +224,11 @@ const createQuery = <R, P extends unknown[]>(
222224
};
223225

224226
const debouncedRun =
225-
!isNil(debounceInterval) && debounce(_run, debounceInterval!);
227+
!isNil(debounceInterval) &&
228+
debounce(_run, debounceInterval!, debounceOptions);
226229
const throttledRun =
227230
!isNil(throttleInterval) &&
228-
throttle(_run, throttleInterval!, {
229-
leading: true,
230-
trailing: true,
231-
});
231+
throttle(_run, throttleInterval!, throttleOptions);
232232

233233
const run = (...args: P) => {
234234
clearAllTimer();

Diff for: src/core/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ export interface InnerQueryState<R, P extends unknown[]>
4141
unmount: () => void;
4242
}
4343

44+
interface DebounceOptions {
45+
leading?: boolean;
46+
trailing?: boolean;
47+
maxWait?: number;
48+
}
49+
type ThrottleOptions = Omit<DebounceOptions, 'maxWait'>;
50+
4451
export interface GlobalOptions
4552
// usePagination config
4653
extends PaginationExtendsOption,
@@ -51,6 +58,8 @@ export interface GlobalOptions
5158
pollingWhenHidden?: boolean;
5259
pollingWhenOffline?: boolean;
5360
debounceInterval?: number;
61+
debounceOptions?: DebounceOptions;
62+
throttleOptions?: ThrottleOptions;
5463
throttleInterval?: number;
5564
refreshOnWindowFocus?: boolean;
5665
refocusTimespan?: number;

0 commit comments

Comments
 (0)