Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加pollingLoading状态返回来区分polling请求和非polling请求的loading状态 #2564

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions packages/hooks/src/useRequest/__tests__/usePollingPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
});
});
6 changes: 3 additions & 3 deletions packages/hooks/src/useRequest/doc/polling/demo/polling.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRequest } from 'ahooks';
import React from 'react';
import React, { useState } from 'react';
import Mock from 'mockjs';

function getUsername() {
Expand All @@ -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 (
<>
<p>Username: {loading ? 'Loading' : data}</p>
<p>Username: {pollingLoading ? 'PollingLoading' : loading ? 'Loading' : data}</p>
<button type="button" onClick={run}>
start
</button>
Expand Down
11 changes: 6 additions & 5 deletions packages/hooks/src/useRequest/doc/polling/polling.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<TData>` |
| 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<TData>` |
| cancel | Stop polling | `() => void` |

### Options

Expand Down
11 changes: 6 additions & 5 deletions packages/hooks/src/useRequest/doc/polling/polling.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ const { data, run, cancel } = useRequest(getUsername, {

### Return

| 参数 | 说明 | 类型 |
| -------- | -------- | ---------------------------------------- |
| run | 启动轮询 | `(...params: TParams) => void` |
| runAsync | 启动轮询 | `(...params: TParams) => Promise<TData>` |
| cancel | 停止轮询 | `() => void` |
| 参数 | 说明 | 类型 |
| -------------- | -------------------- | ---------------------------------------- |
| PollingLoading | 是否正在进行轮询请求 | `boolean` |
| run | 启动轮询 | `(...params: TParams) => void` |
| runAsync | 启动轮询 | `(...params: TParams) => Promise<TData>` |
| cancel | 停止轮询 | `() => void` |

### Options

Expand Down
6 changes: 4 additions & 2 deletions packages/hooks/src/useRequest/src/Fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export default class Fetch<TData, TParams extends any[]> {
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;
Expand All @@ -120,7 +121,8 @@ export default class Fetch<TData, TParams extends any[]> {
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;
Expand Down
14 changes: 13 additions & 1 deletion packages/hooks/src/useRequest/src/plugins/usePollingPlugin.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -11,6 +11,7 @@ const usePollingPlugin: Plugin<any, any[]> = (
const timerRef = useRef<Timeout>();
const unsubscribeRef = useRef<() => void>();
const countRef = useRef<number>(0);
const pollingLoadingRef = useRef(false);

const stopPolling = () => {
if (timerRef.current) {
Expand All @@ -32,12 +33,17 @@ const usePollingPlugin: Plugin<any, any[]> = (
return {
onBefore: () => {
stopPolling();
return {
pollingLoading: pollingLoadingRef.current,
};
},
onError: () => {
countRef.current += 1;
pollingLoadingRef.current = false;
},
onSuccess: () => {
countRef.current = 0;
pollingLoadingRef.current = false;
},
onFinally: () => {
if (
Expand All @@ -49,18 +55,24 @@ const usePollingPlugin: Plugin<any, any[]> = (
// 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;
},
};
};
Expand Down
10 changes: 6 additions & 4 deletions packages/hooks/src/useRequest/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface FetchState<TData, TParams extends any[]> {
params?: TParams;
data?: TData;
error?: Error;
pollingLoading?: boolean;
}

export interface PluginReturn<TData, TParams extends any[]> {
Expand Down Expand Up @@ -94,10 +95,10 @@ export interface Options<TData, TParams extends any[]> {
}

export type Plugin<TData, TParams extends any[]> = {
(fetchInstance: Fetch<TData, TParams>, options: Options<TData, TParams>): PluginReturn<
TData,
TParams
>;
(
fetchInstance: Fetch<TData, TParams>,
options: Options<TData, TParams>,
): PluginReturn<TData, TParams>;
onInit?: (options: Options<TData, TParams>) => Partial<FetchState<TData, TParams>>;
};

Expand All @@ -119,6 +120,7 @@ export interface Result<TData, TParams extends any[]> {
run: Fetch<TData, TParams>['run'];
runAsync: Fetch<TData, TParams>['runAsync'];
mutate: Fetch<TData, TParams>['mutate'];
pollingLoading?: boolean;
}

export type Timeout = ReturnType<typeof setTimeout>;
1 change: 1 addition & 0 deletions packages/hooks/src/useRequest/src/useRequestImplement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function useRequestImplement<TData, TParams extends any[]>(
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)),
Expand Down