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);
+    });
 });