From 98af58cd899960593465b62bc3c649371be31aaa Mon Sep 17 00:00:00 2001 From: mortalYoung <yangwei1@outlook.com> Date: Mon, 20 Jan 2025 14:17:00 +0800 Subject: [PATCH 1/2] fix(useList): support clearData --- src/useList/demos/query.tsx | 93 +++++++++++++++++++++++++++++++++++++ src/useList/index.md | 1 + src/useList/index.ts | 50 +++++++++++++++++--- 3 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 src/useList/demos/query.tsx diff --git a/src/useList/demos/query.tsx b/src/useList/demos/query.tsx new file mode 100644 index 000000000..22ee1d0d2 --- /dev/null +++ b/src/useList/demos/query.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { Input, Result, Table } from 'antd'; +import { Empty, useList } from 'dt-react-component'; + +import getMockData, { type MockData } from './data'; + +export default () => { + const { error, params, loading, data, mutate, clearData } = useList< + MockData, + { current: number; pageSize: number; search?: string } + >( + (params) => { + return new Promise<{ + data: MockData[]; + total: number; + }>((resolve) => { + setTimeout(() => { + resolve(getMockData(params)); + }, 1000); + }); + }, + () => ({ + current: 1, + pageSize: 20, + }) + ); + + if (error) return <Result status={500} />; + + return ( + <> + <Input.Search + value={params.search} + onChange={(e) => { + mutate({ search: e.target.value }, { revalidate: false }); + clearData(); + }} + onSearch={() => mutate()} + style={{ marginBottom: 12 }} + /> + <Table + columns={[ + { + key: 'name', + title: 'name', + dataIndex: 'name', + }, + { + key: 'address', + title: 'address', + dataIndex: 'address', + }, + { + key: 'company', + title: 'company', + dataIndex: 'company', + }, + { + key: 'gender', + title: 'gender', + dataIndex: 'gender', + }, + { + key: 'weight', + title: 'weight', + dataIndex: 'weight', + }, + ]} + onChange={(pagination) => + mutate({ current: pagination.current, pageSize: pagination.pageSize }) + } + size="small" + scroll={{ y: 200 }} + dataSource={data} + pagination={{ + current: params.current, + pageSize: params.pageSize, + total: params.total, + }} + locale={{ + emptyText: () => + loading ? ( + <Empty type="search" active description="搜索中" /> + ) : ( + <Empty type="default" /> + ), + }} + rowKey="uuid" + bordered + /> + </> + ); +}; diff --git a/src/useList/index.md b/src/useList/index.md index 660a9754f..1c3c2c7be 100644 --- a/src/useList/index.md +++ b/src/useList/index.md @@ -17,6 +17,7 @@ toc: content <code src="./demos/options.tsx" title="相关配置" description="设置 immediate 值防止初始化的时候进行请求"></code> <code src="./demos/mutate.tsx" title="相关配置" description="用 undefined 覆盖 prevPrams 时,需采用 functional 的写法 "></code> <code src="./demos/mutateOptions" title="mutate相关配置" description="revalidate 修改后请求数据,clearData 请求前清除数据"></code> +<code src="./demos/query" title="搜索" description="支持修改后重置搜索"></code> ## API diff --git a/src/useList/index.ts b/src/useList/index.ts index de5406374..3b923899e 100644 --- a/src/useList/index.ts +++ b/src/useList/index.ts @@ -21,11 +21,45 @@ export interface IUseListOptions { immediate?: boolean; } +/** + * 返回值 + */ +export interface UseListResponseState< + T extends Record<string, any>, + P extends Record<string, any> +> { + /** + * 请求是否执行中 + */ + loading: boolean; + /** + * 请求的相关参数以及结果数据的总数 + */ + params: P & { total: number }; + /** + * 错误信息 + */ + error?: Error; + /** + * 返回的数据 + */ + data: T[]; + mutate: (params?: Partial<P> | ((prev: P) => P), options?: IMutateOptions) => void; + /** + * 清空所有数据和状态 + */ + clear: () => void; + /** + * 清空数据 + */ + clearData: () => void; +} + export default function useList<T extends Record<string, any>, P extends Record<string, any>>( fetcher: Fetcher<T, P>, initialParams: P | (() => P), rawOptions: IUseListOptions = { immediate: true } -) { +): UseListResponseState<T, P> { const [error, setError] = useState<Error | undefined>(undefined); const [data, setData] = useState<T[]>([]); const [total, setTotal] = useState(0); @@ -62,20 +96,22 @@ export default function useList<T extends Record<string, any>, P extends Record< if (nextOptions.revalidate) { if (nextOptions.clearData) { - setData([]); - setTotal(0); - setError(undefined); + clearData(); } performFetch(tmp); } }; - const clear = () => { + const clearData = () => { setData([]); setTotal(0); + setError(undefined); + }; + + const clear = () => { + clearData(); setParams(initialParams); setLoading(false); - setError(undefined); }; useEffect(() => { @@ -84,5 +120,5 @@ export default function useList<T extends Record<string, any>, P extends Record< } }, []); - return { loading, params: { ...params, total }, error, data, mutate, clear }; + return { loading, params: { ...params, total }, error, data, mutate, clear, clearData }; } From fb6d2d1008deba68aca5876ca99c3872106d216e Mon Sep 17 00:00:00 2001 From: mortalYoung <yangwei1@outlook.com> Date: Thu, 23 Jan 2025 17:02:22 +0800 Subject: [PATCH 2/2] test(useList): add clearData test --- src/useList/__tests__/useList.test.ts | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/useList/__tests__/useList.test.ts b/src/useList/__tests__/useList.test.ts index 965b765c0..f9b554219 100644 --- a/src/useList/__tests__/useList.test.ts +++ b/src/useList/__tests__/useList.test.ts @@ -154,4 +154,42 @@ describe('Test useList hook', () => { expect(result.current.params.total).toBe(0); expect(result.current.error).toBe(undefined); }); + + it('Should ONLY clear data while not reset loading', async () => { + const fetcher = jest.fn().mockImplementation( + () => + new Promise((resolve) => { + setTimeout(() => { + resolve({ + total: 1, + data: [{ uuid: 1 }], + }); + }, 1000); + }) + ); + + const { result } = renderHook(() => + useList(fetcher, { current: 1, pageSize: 20, search: '' }, { immediate: false }) + ); + act(() => { + result.current.mutate({ current: 2 }); + }); + + expect(result.current.loading).toBe(true); + expect(result.current.params.current).toBe(2); + + act(() => { + result.current.clearData(); + }); + // clearData won't reset params and loading status + expect(result.current.loading).toBe(true); + expect(result.current.params.current).toBe(2); + + act(() => { + result.current.clear(); + }); + // clear method will clear all data including params and loading status + expect(result.current.loading).toBe(false); + expect(result.current.params.current).toBe(1); + }); });