Skip to content

Commit

Permalink
feat(useRequest): add skipStaleTime in refreshOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
BastKakrolot committed Nov 16, 2023
1 parent 810fcf2 commit c6b395c
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 28 deletions.
35 changes: 35 additions & 0 deletions packages/hooks/src/useRequest/__tests__/useCachePlugin.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,41 @@ describe('useCachePlugin', () => {
expect(hook3.result.current.loading).toBe(false);
});

it('useRequest skipStaleTime should work', async () => {
await testCacheKey({
cacheKey: 'testSkipStaleTime',
staleTime: 3000,
});
jest.advanceTimersByTime(1000);
const hook2 = setup(request, {
cacheKey: 'testSkipStaleTime',
staleTime: 3000,
});
act(() => {
hook2.result.current.refresh();
});
expect(hook2.result.current.loading).toBe(false);
expect(hook2.result.current.data).toBe('success');
act(() => {
hook2.result.current.refresh({
skipStaleTime: true,
});
});
expect(hook2.result.current.loading).toBe(true);
await waitFor(() => expect(hook2.result.current.data).toBe('success'));
act(() => {
hook2.result.current.refresh();
});
expect(hook2.result.current.loading).toBe(false);
act(() => {
hook2.result.current.refresh({
skipStaleTime: true,
});
});
expect(hook2.result.current.loading).toBe(true);
hook2.unmount();
});

it('useRequest cacheTime should work', async () => {
await testCacheKey({
cacheKey: 'testCacheTime',
Expand Down
10 changes: 8 additions & 2 deletions packages/hooks/src/useRequest/doc/basic/basic.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const {
params: TParams || [],
run: (...params: TParams) => void,
runAsync: (...params: TParams) => Promise<TData>,
refresh: () => void,
refresh: (options?: RefreshOptions) => void,
refreshAsync: () => Promise<TData>,
mutate: (data?: TData | ((oldData?: TData) => (TData | undefined))) => void,
cancel: () => void,
Expand All @@ -155,7 +155,7 @@ const {
| params | An array of parameters for the service being executed. For example, you triggered `run(1, 2, 3)`, then params is equal to `[1, 2, 3]` | `TParams` \| `[]` |
| run | <ul><li> Manually trigger the execution of the service, and the parameters will be passed to the service</li><li>Automatic handling of exceptions, feedback through `onError`</li></ul> | `(...params : TParams) => void` |
| runAsync | The usage is the same as `run`, but it returns a Promise, so you need to handle the exception yourself. | `(...params: TParams) => Promise<TData>` |
| refresh | Use the last params, call `run` again | `() => void` |
| refresh | Use the last params, call `run` again | `(options?:RefreshOptions) => void` |
| refreshAsync | Use the last params, call `runAsync` again | `() => Promise<TData>` |
| mutate | Mutate `data` directly | `(data?: TData / ((oldData?: TData) => (TData / undefined))) => void` |
| cancel | Ignore the current promise response | `() => void` |
Expand All @@ -171,6 +171,12 @@ const {
| onError | Triggered when service reject | `(e: Error, params: TParams) => void` | - |
| onFinally | Triggered when service execution is complete | `(params: TParams, data?: TData, e?: Error) => void` | - |

### RefreshOptions

| 参数 | 说明 | 类型 | 默认值 |
| ------------- | ---------------- | --------- | ------- |
| skipStaleTime | skip `staleTime` | `boolean` | `false` |

### Trigger

| EnumValue | Description |
Expand Down
10 changes: 8 additions & 2 deletions packages/hooks/src/useRequest/doc/basic/basic.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const {
params: TParams || [],
run: (...params: TParams) => void,
runAsync: (...params: TParams) => Promise<TData>,
refresh: () => void,
refresh: (options?: RefreshOptions) => void,
refreshAsync: () => Promise<TData>,
mutate: (data?: TData | ((oldData?: TData) => (TData | undefined))) => void,
cancel: () => void,
Expand All @@ -155,7 +155,7 @@ const {
| params | 当次执行的 service 的参数数组。比如你触发了 `run(1, 2, 3)`,则 params 等于 `[1, 2, 3]` | `TParams` \| `[]` |
| run | <ul><li> 手动触发 service 执行,参数会传递给 service</li><li>异常自动处理,通过 `onError` 反馈</li></ul> | `(...params: TParams) => void` |
| runAsync |`run` 用法一致,但返回的是 Promise,需要自行处理异常。 | `(...params: TParams) => Promise<TData>` |
| refresh | 使用上一次的 params,重新调用 `run` | `() => void` |
| refresh | 使用上一次的 params,重新调用 `run` | `(options?:RefreshOptions) => void` |
| refreshAsync | 使用上一次的 params,重新调用 `runAsync` | `() => Promise<TData>` |
| mutate | 直接修改 `data` | `(data?: TData / ((oldData?: TData) => (TData / undefined))) => void` |
| cancel | 忽略当前 Promise 的响应 | `() => void` |
Expand All @@ -171,6 +171,12 @@ const {
| onError | service reject 时触发 | `(e: Error, params: TParams) => void` | - |
| onFinally | service 执行完成时触发 | `(params: TParams, data?: TData, e?: Error) => void` | - |

### RefreshOptions

| 参数 | 说明 | 类型 | 默认值 |
| ------------- | -------------- | --------- | ------- |
| skipStaleTime | 跳过 staleTime | `boolean` | `false` |

### Trigger

| 枚举值 | 说明 |
Expand Down
4 changes: 2 additions & 2 deletions packages/hooks/src/useRequest/doc/basic/demo/lifeCycle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function editUsername(username: string): Promise<void> {
export default () => {
const [state, setState] = useState('');

const { loading, run, refresh, refreshAsync } = useRequest(editUsername, {
const { loading, run, refresh } = useRequest(editUsername, {
manual: true,
onBefore: (params, trigger) => {
message.info(`${trigger} :Start Request: ${params[0]}`);
Expand Down Expand Up @@ -45,7 +45,7 @@ export default () => {
<button disabled={loading} type="button" onClick={() => run(state)}>
{loading ? 'Loading' : 'Edit'}
</button>
<button disabled={loading} type="button" onClick={refresh}>
<button disabled={loading} type="button" onClick={() => refresh()} style={{ marginLeft: 16 }}>
refresh
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/hooks/src/useRequest/doc/basic/demo/refresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default () => {
return (
<div>
<p>Username: {data}</p>
<button onClick={refresh} type="button">
<button onClick={() => refresh()} type="button">
Refresh
</button>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/hooks/src/useRequest/doc/cache/cache.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ In the following example, we set the `cacheKey`. When the component is loaded fo

By setting `staleTime`, we can specify the data retention time, during which time the request will not be re-run. The following example sets a fresh time of 5s, you can experience the effect by clicking the button

When you need to force new data, you can configure `skipStaleTime` to `true` in `refresh`, which will skip `staleTime` to refresh
<code src="./demo/staleTime.tsx" />

### Data sharing
Expand Down
2 changes: 2 additions & 0 deletions packages/hooks/src/useRequest/doc/cache/cache.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ group:

通过设置 `staleTime`,我们可以指定数据新鲜时间,在这个时间内,不会重新发起请求。下面的示例设置了 5s 的新鲜时间,你可以通过点击按钮来体验效果

当你需要强制获取新数据时可以在 `refresh` 中配置 `skipStaleTime``true`,将跳过 `staleTime` 进行刷新

<code src="./demo/staleTime.tsx" />

### 数据共享
Expand Down
19 changes: 17 additions & 2 deletions packages/hooks/src/useRequest/doc/cache/demo/staleTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async function getArticle(): Promise<{ data: string; time: number }> {
}

const Article = () => {
const { data, loading } = useRequest(getArticle, {
const { data, loading, refresh } = useRequest(getArticle, {
cacheKey: 'staleTime-demo',
staleTime: 5000,
});
Expand All @@ -25,6 +25,21 @@ const Article = () => {
}
return (
<>
<br />
<button type="button" onClick={() => refresh()}>
refresh
</button>
<button
type="button"
onClick={() =>
refresh({
skipStaleTime: true,
})
}
style={{ marginLeft: '10px' }}
>
refresh:skipStaleTime
</button>
<p>Background loading: {loading ? 'true' : 'false'}</p>
<p>Latest request time: {data?.time}</p>
<p>{data?.data}</p>
Expand All @@ -36,7 +51,7 @@ export default () => {
const [state, { toggle }] = useBoolean();
return (
<div>
<button type="button" onClick={() => toggle()}>
<button type="button" style={{ marginBottom: '10px' }} onClick={() => toggle()}>
show/hidden
</button>
{state && <Article />}
Expand Down
53 changes: 41 additions & 12 deletions packages/hooks/src/useRequest/src/Fetch.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
/* eslint-disable @typescript-eslint/no-parameter-properties */
import { isFunction } from '../../utils';
import type { MutableRefObject } from 'react';
import type { FetchState, Options, PluginReturn, Service, Subscribe } from './types';
import type {
FetchState,
Options,
PluginReturn,
Service,
Subscribe,
RefreshOptions,
TempConfig,
} from './types';
import { Trigger } from './types';
import { omit } from 'lodash-es';

const getDefaultTempConfig: () => TempConfig = () => ({
trigger: undefined,
skipStaleTime: false,
});

export default class Fetch<TData, TParams extends any[]> {
pluginImpls: PluginReturn<TData, TParams>[];

count: number = 0;
trigger: Trigger | undefined;

tempConfig: TempConfig = getDefaultTempConfig();

state: FetchState<TData, TParams> = {
loading: false,
Expand Down Expand Up @@ -36,16 +52,28 @@ export default class Fetch<TData, TParams extends any[]> {
};
this.subscribe();
}

setTempConfig(config?: Partial<TempConfig>) {
this.tempConfig = {
...this.tempConfig,
// 不允许修改触发器类型
...omit(config, 'trigger'),
};
}
getTempConfig(key: keyof TempConfig | undefined) {
if (key) {
return this.tempConfig[key];
}
return this.tempConfig;
}
setTrigger(triggerValue: Trigger) {
if (this.trigger) {
if (this.tempConfig.trigger) {
return;
}
this.trigger = triggerValue;
this.tempConfig.trigger = triggerValue;
}

clearTrigger() {
this.trigger = undefined;
resetTempConfig() {
this.tempConfig = getDefaultTempConfig();
}

runPluginHandler(event: keyof PluginReturn<TData, TParams>, ...rest: any[]) {
Expand All @@ -67,7 +95,7 @@ export default class Fetch<TData, TParams extends any[]> {

// stop request
if (stopNow) {
this.clearTrigger();
this.resetTempConfig();
return new Promise(() => {});
}

Expand All @@ -79,12 +107,12 @@ export default class Fetch<TData, TParams extends any[]> {

// return now
if (returnNow) {
this.clearTrigger();
this.resetTempConfig();
return Promise.resolve(state.data);
}

this.options.onBefore?.(params, this.trigger);
this.clearTrigger();
this.options.onBefore?.(params, this.tempConfig.trigger);
this.resetTempConfig();
try {
// replace service
let { servicePromise } = this.runPluginHandler('onRequest', this.serviceRef.current, params);
Expand Down Expand Up @@ -160,7 +188,8 @@ export default class Fetch<TData, TParams extends any[]> {
this.runPluginHandler('onCancel');
}

refresh() {
refresh(options?: RefreshOptions) {
this.setTempConfig(options);
this.setTrigger(Trigger.REFRESH);
// @ts-ignore
this.run(...(this.state.params || []));
Expand Down
14 changes: 8 additions & 6 deletions packages/hooks/src/useRequest/src/plugins/useCachePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const useCachePlugin: Plugin<any, any[]> = (
return getCache(key);
};

const _isFresh = (cacheData: CachedData) =>
staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime;

useCreation(() => {
if (!cacheKey) {
return;
Expand All @@ -47,12 +50,12 @@ const useCachePlugin: Plugin<any, any[]> = (
if (cacheData && Object.hasOwnProperty.call(cacheData, 'data')) {
fetchInstance.state.data = cacheData.data;
fetchInstance.state.params = cacheData.params;
if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
if (_isFresh(cacheData)) {
fetchInstance.state.loading = false;
}
}

// subscribe same cachekey update, trigger update
// subscribe same cacheKey update, trigger update
unSubscribeRef.current = subscribe(cacheKey, (data) => {
fetchInstance.setState({ data });
});
Expand All @@ -73,9 +76,8 @@ const useCachePlugin: Plugin<any, any[]> = (
if (!cacheData || !Object.hasOwnProperty.call(cacheData, 'data')) {
return {};
}

// If the data is fresh, stop request
if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
// If the data is fresh && don't need to skip stale time, stop request
if (_isFresh(cacheData) && !fetchInstance.getTempConfig('skipStaleTime')) {
return {
loading: false,
data: cacheData?.data,
Expand Down Expand Up @@ -105,7 +107,7 @@ const useCachePlugin: Plugin<any, any[]> = (
},
onSuccess: (data, params) => {
if (cacheKey) {
// cancel subscribe, avoid trgger self
// cancel subscribe, avoid trigger self
unSubscribeRef.current?.();
_setCache(cacheKey, {
data,
Expand Down
7 changes: 6 additions & 1 deletion packages/hooks/src/useRequest/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export enum Trigger {
// 来自重试
RETRY = 'RETRY',
}

export interface RefreshOptions {
skipStaleTime?: boolean;
}
export interface TempConfig extends RefreshOptions {
trigger: Trigger | undefined;
}
export interface PluginReturn<TData, TParams extends any[]> {
onBefore?: (params: TParams) =>
| ({
Expand Down

0 comments on commit c6b395c

Please sign in to comment.