diff --git a/packages/hooks/src/useRequest/__tests__/usePollingPlugin.test.ts b/packages/hooks/src/useRequest/__tests__/usePollingPlugin.test.ts
index b7a01c4e9f..a067b85452 100644
--- a/packages/hooks/src/useRequest/__tests__/usePollingPlugin.test.ts
+++ b/packages/hooks/src/useRequest/__tests__/usePollingPlugin.test.ts
@@ -116,4 +116,79 @@ describe('usePollingPlugin', () => {
});
await waitFor(() => expect(errorCallback).toHaveBeenCalledTimes(5));
});
+
+ it('usePollingPlugin pollingInterval=100 pollingWhenHidden=true should return pollingLoading', async () => {
+ let hook;
+ const callback = jest.fn();
+ act(() => {
+ hook = setUp(
+ () => {
+ callback();
+ return request(1);
+ },
+ {
+ pollingInterval: 100,
+ pollingWhenHidden: true,
+ },
+ );
+ });
+ expect(hook.result.current.loading).toBe(true);
+ // Initial state: pollingLoading should be false
+ expect(hook.result.current.pollingLoading).toBe(false);
+
+ // first request completed
+ act(() => {
+ jest.runAllTimers();
+ });
+ await waitFor(() => expect(hook.result.current.loading).toBe(false));
+ expect(callback).toHaveBeenCalledTimes(1);
+
+ // trigger the next polling request
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(hook.result.current.loading).toBe(true);
+ // pollingLoading: true => false
+ expect(hook.result.current.pollingLoading).toBe(true);
+ await waitFor(() => expect(hook.result.current.pollingLoading).toBe(false));
+ expect(hook.result.current.data).toBe('success');
+ expect(callback).toHaveBeenCalledTimes(2);
+
+ act(() => {
+ jest.runAllTimers();
+ });
+ // pollingLoading: true => false, synchronized with loading
+ expect(hook.result.current.loading).toBe(true);
+ expect(hook.result.current.pollingLoading).toBe(true);
+ await waitFor(() => expect(hook.result.current.loading).toBe(false));
+ expect(hook.result.current.pollingLoading).toBe(false);
+ expect(hook.result.current.data).toBe('success');
+ expect(callback).toHaveBeenCalledTimes(3);
+
+ act(() => {
+ hook.result.current.cancel();
+ });
+ expect(hook.result.current.pollingLoading).toBe(false);
+
+ // should be false in manual run
+ act(() => {
+ hook.result.current.run();
+ });
+ expect(hook.result.current.loading).toBe(true);
+ expect(hook.result.current.pollingLoading).toBe(false);
+ await waitFor(() => expect(callback).toHaveBeenCalledTimes(4));
+
+ // first request completed
+ act(() => {
+ jest.runAllTimers();
+ });
+ await waitFor(() => expect(hook.result.current.loading).toBe(false));
+
+ act(() => {
+ jest.runAllTimers();
+ });
+ // pollingLoading: true => false
+ expect(hook.result.current.pollingLoading).toBe(true);
+ await waitFor(() => expect(hook.result.current.pollingLoading).toBe(false));
+ });
});
diff --git a/packages/hooks/src/useRequest/doc/polling/demo/polling.tsx b/packages/hooks/src/useRequest/doc/polling/demo/polling.tsx
index dcf8f0facb..02a5bcaa3f 100644
--- a/packages/hooks/src/useRequest/doc/polling/demo/polling.tsx
+++ b/packages/hooks/src/useRequest/doc/polling/demo/polling.tsx
@@ -1,5 +1,5 @@
import { useRequest } from 'ahooks';
-import React from 'react';
+import React, { useState } from 'react';
import Mock from 'mockjs';
function getUsername() {
@@ -12,14 +12,14 @@ function getUsername() {
}
export default () => {
- const { data, loading, run, cancel } = useRequest(getUsername, {
+ const { data, loading, run, cancel, pollingLoading } = useRequest(getUsername, {
pollingInterval: 1000,
pollingWhenHidden: false,
});
return (
<>
-
Username: {loading ? 'Loading' : data}
+ Username: {pollingLoading ? 'PollingLoading' : loading ? 'Loading' : data}
diff --git a/packages/hooks/src/useRequest/doc/polling/polling.en-US.md b/packages/hooks/src/useRequest/doc/polling/polling.en-US.md
index 56c69db578..3e70feda74 100644
--- a/packages/hooks/src/useRequest/doc/polling/polling.en-US.md
+++ b/packages/hooks/src/useRequest/doc/polling/polling.en-US.md
@@ -40,11 +40,12 @@ You can experience the effect through the following example.
### Return
-| Property | Description | Type |
-| -------- | ------------- | ---------------------------------------- |
-| run | Start polling | `(...params: TParams) => void` |
-| runAsync | Start polling | `(...params: TParams) => Promise` |
-| cancel | Stop polling | `() => void` |
+| Property | Description | Type |
+| -------------- | ---------------------------------- | ---------------------------------------- |
+| PollingLoading | In the polling request in progress | `boolean` |
+| run | Start polling | `(...params: TParams) => void` |
+| runAsync | Start polling | `(...params: TParams) => Promise` |
+| cancel | Stop polling | `() => void` |
### Options
diff --git a/packages/hooks/src/useRequest/doc/polling/polling.zh-CN.md b/packages/hooks/src/useRequest/doc/polling/polling.zh-CN.md
index b124f49d02..8e6aa4f00d 100644
--- a/packages/hooks/src/useRequest/doc/polling/polling.zh-CN.md
+++ b/packages/hooks/src/useRequest/doc/polling/polling.zh-CN.md
@@ -40,11 +40,12 @@ const { data, run, cancel } = useRequest(getUsername, {
### Return
-| 参数 | 说明 | 类型 |
-| -------- | -------- | ---------------------------------------- |
-| run | 启动轮询 | `(...params: TParams) => void` |
-| runAsync | 启动轮询 | `(...params: TParams) => Promise` |
-| cancel | 停止轮询 | `() => void` |
+| 参数 | 说明 | 类型 |
+| -------------- | -------------------- | ---------------------------------------- |
+| PollingLoading | 是否正在进行轮询请求 | `boolean` |
+| run | 启动轮询 | `(...params: TParams) => void` |
+| runAsync | 启动轮询 | `(...params: TParams) => Promise` |
+| cancel | 停止轮询 | `() => void` |
### Options
diff --git a/packages/hooks/src/useRequest/src/Fetch.ts b/packages/hooks/src/useRequest/src/Fetch.ts
index 73ceee6004..5a4357bbe4 100644
--- a/packages/hooks/src/useRequest/src/Fetch.ts
+++ b/packages/hooks/src/useRequest/src/Fetch.ts
@@ -99,7 +99,8 @@ export default class Fetch {
this.options.onFinally?.(params, res, undefined);
if (currentCount === this.count) {
- this.runPluginHandler('onFinally', params, res, undefined);
+ const state = this.runPluginHandler('onFinally', params, res, undefined);
+ this.setState({ ...state });
}
return res;
@@ -120,7 +121,8 @@ export default class Fetch {
this.options.onFinally?.(params, undefined, error);
if (currentCount === this.count) {
- this.runPluginHandler('onFinally', params, undefined, error);
+ const state = this.runPluginHandler('onFinally', params, undefined, error);
+ this.setState({ ...state });
}
throw error;
diff --git a/packages/hooks/src/useRequest/src/plugins/usePollingPlugin.ts b/packages/hooks/src/useRequest/src/plugins/usePollingPlugin.ts
index bf9a30b47c..9596ff05d7 100644
--- a/packages/hooks/src/useRequest/src/plugins/usePollingPlugin.ts
+++ b/packages/hooks/src/useRequest/src/plugins/usePollingPlugin.ts
@@ -1,4 +1,4 @@
-import { useRef } from 'react';
+import { useRef, useState } from 'react';
import useUpdateEffect from '../../../useUpdateEffect';
import type { Plugin, Timeout } from '../types';
import isDocumentVisible from '../utils/isDocumentVisible';
@@ -11,6 +11,7 @@ const usePollingPlugin: Plugin = (
const timerRef = useRef();
const unsubscribeRef = useRef<() => void>();
const countRef = useRef(0);
+ const pollingLoadingRef = useRef(false);
const stopPolling = () => {
if (timerRef.current) {
@@ -32,12 +33,17 @@ const usePollingPlugin: Plugin = (
return {
onBefore: () => {
stopPolling();
+ return {
+ pollingLoading: pollingLoadingRef.current,
+ };
},
onError: () => {
countRef.current += 1;
+ pollingLoadingRef.current = false;
},
onSuccess: () => {
countRef.current = 0;
+ pollingLoadingRef.current = false;
},
onFinally: () => {
if (
@@ -49,18 +55,24 @@ const usePollingPlugin: Plugin = (
// if pollingWhenHidden = false && document is hidden, then stop polling and subscribe revisible
if (!pollingWhenHidden && !isDocumentVisible()) {
unsubscribeRef.current = subscribeReVisible(() => {
+ pollingLoadingRef.current = true;
fetchInstance.refresh();
});
} else {
+ pollingLoadingRef.current = true;
fetchInstance.refresh();
}
}, pollingInterval);
} else {
countRef.current = 0;
}
+ return {
+ pollingLoading: pollingLoadingRef.current,
+ };
},
onCancel: () => {
stopPolling();
+ pollingLoadingRef.current = false;
},
};
};
diff --git a/packages/hooks/src/useRequest/src/types.ts b/packages/hooks/src/useRequest/src/types.ts
index 442a2dff73..5034185087 100644
--- a/packages/hooks/src/useRequest/src/types.ts
+++ b/packages/hooks/src/useRequest/src/types.ts
@@ -12,6 +12,7 @@ export interface FetchState {
params?: TParams;
data?: TData;
error?: Error;
+ pollingLoading?: boolean;
}
export interface PluginReturn {
@@ -94,10 +95,10 @@ export interface Options {
}
export type Plugin = {
- (fetchInstance: Fetch, options: Options): PluginReturn<
- TData,
- TParams
- >;
+ (
+ fetchInstance: Fetch,
+ options: Options,
+ ): PluginReturn;
onInit?: (options: Options) => Partial>;
};
@@ -119,6 +120,7 @@ export interface Result {
run: Fetch['run'];
runAsync: Fetch['runAsync'];
mutate: Fetch['mutate'];
+ pollingLoading?: boolean;
}
export type Timeout = ReturnType;
diff --git a/packages/hooks/src/useRequest/src/useRequestImplement.ts b/packages/hooks/src/useRequest/src/useRequestImplement.ts
index d65cfba6cb..2defec9c15 100644
--- a/packages/hooks/src/useRequest/src/useRequestImplement.ts
+++ b/packages/hooks/src/useRequest/src/useRequestImplement.ts
@@ -64,6 +64,7 @@ function useRequestImplement(
data: fetchInstance.state.data,
error: fetchInstance.state.error,
params: fetchInstance.state.params || [],
+ pollingLoading: fetchInstance.state.pollingLoading,
cancel: useMemoizedFn(fetchInstance.cancel.bind(fetchInstance)),
refresh: useMemoizedFn(fetchInstance.refresh.bind(fetchInstance)),
refreshAsync: useMemoizedFn(fetchInstance.refreshAsync.bind(fetchInstance)),