From da0f03b118b9a0852fda296c3466a3d7199dec56 Mon Sep 17 00:00:00 2001
From: LuckyFBB <976060700@qq.com>
Date: Sat, 10 Oct 2026 14:50:33 +0800
Subject: [PATCH 1/2] fix(uselist): correct useList parameter merging
fix #632
---
src/useList/__tests__/useList.test.ts | 60 +++++++++++++++++++++++++++
src/useList/demos/mutate.tsx | 4 +-
src/useList/demos/mutateOptions.tsx | 4 +-
src/useList/index.md | 2 +-
src/useList/index.ts | 22 +++++++++-
5 files changed, 83 insertions(+), 9 deletions(-)
diff --git a/src/useList/__tests__/useList.test.ts b/src/useList/__tests__/useList.test.ts
index f9b554219..f1dd8fce1 100644
--- a/src/useList/__tests__/useList.test.ts
+++ b/src/useList/__tests__/useList.test.ts
@@ -67,6 +67,66 @@ describe('Test useList hook', () => {
expect(result.current.params).toEqual(expect.objectContaining({ search: 'test' }));
});
+ it('Should merge object params while replacing arrays and nullable values', () => {
+ type Params = {
+ current: number;
+ filters: number[];
+ search?: string;
+ nullable: string | null;
+ nested: {
+ first?: number;
+ second?: number;
+ filters?: number[];
+ search?: string;
+ };
+ };
+ const fetcher = jest.fn().mockResolvedValue({ total: 0, data: [] });
+ const { result } = renderHook(() =>
+ useList(
+ fetcher,
+ {
+ current: 1,
+ filters: [1, 2, 3],
+ search: 'test',
+ nullable: 'value',
+ nested: {
+ first: 1,
+ second: 2,
+ filters: [1, 2, 3],
+ search: 'nested',
+ },
+ } as Params,
+ { immediate: false }
+ )
+ );
+
+ act(() => {
+ result.current.mutate(
+ {
+ filters: [4, 5],
+ search: undefined,
+ nullable: null,
+ nested: { first: 3, filters: [4, 5], search: undefined },
+ },
+ { revalidate: false }
+ );
+ });
+
+ expect(result.current.params).toEqual({
+ current: 1,
+ filters: [4, 5],
+ search: undefined,
+ nullable: null,
+ nested: {
+ first: 3,
+ second: 2,
+ filters: [4, 5],
+ search: undefined,
+ },
+ total: 0,
+ });
+ });
+
it('Should support get data with current params', () => {
const fetcher = jest.fn().mockResolvedValue({
total: 1,
diff --git a/src/useList/demos/mutate.tsx b/src/useList/demos/mutate.tsx
index 0df448fc1..839a54a7e 100644
--- a/src/useList/demos/mutate.tsx
+++ b/src/useList/demos/mutate.tsx
@@ -32,9 +32,7 @@ export default () => {
const handleReset = async () => {
form.resetFields();
const values = await form.validateFields();
- // 当传入值有 undefined 的时候,采用 functional 的写法。
- // 因为底层使用的 lodash 的 merge,采用赋值写法不会对 undefined 做合并
- mutate((pre) => ({ ...pre, ...values }));
+ mutate({ ...values });
};
return (
diff --git a/src/useList/demos/mutateOptions.tsx b/src/useList/demos/mutateOptions.tsx
index 9ae4bfacd..e3a7bed3e 100644
--- a/src/useList/demos/mutateOptions.tsx
+++ b/src/useList/demos/mutateOptions.tsx
@@ -32,9 +32,7 @@ export default () => {
const handleReset = async () => {
form.resetFields();
const values = await form.validateFields();
- // 当传入值有 undefined 的时候,采用 functional 的写法。
- // 因为底层使用的 lodash 的 merge,采用赋值写法不会对 undefined 做合并
- mutate((pre) => ({ ...pre, ...values }), { revalidate, clearData });
+ mutate({ ...values }, { revalidate, clearData });
};
const [revalidate, setRevalidate] = useState(true);
const [clearData, setClearData] = useState(true);
diff --git a/src/useList/index.md b/src/useList/index.md
index 1c3c2c7be..5c64a66f2 100644
--- a/src/useList/index.md
+++ b/src/useList/index.md
@@ -15,7 +15,7 @@ toc: content
-
+
diff --git a/src/useList/index.ts b/src/useList/index.ts
index 3b923899e..69dcd7d26 100644
--- a/src/useList/index.ts
+++ b/src/useList/index.ts
@@ -1,5 +1,23 @@
import { useEffect, useMemo, useRef, useState } from 'react';
-import { merge } from 'lodash-es';
+import { isPlainObject, merge } from 'lodash-es';
+
+const mergeParams = (
+ previous: Record,
+ next: Record
+): Record => {
+ const result = { ...previous };
+
+ Object.keys(next).forEach((key) => {
+ const previousValue = previous[key];
+ const nextValue = next[key];
+ result[key] =
+ isPlainObject(previousValue) && isPlainObject(nextValue)
+ ? mergeParams(previousValue, nextValue)
+ : nextValue;
+ });
+
+ return result;
+};
export type Fetcher = (params: P) => Promise<{ data: T[]; total: number }>;
@@ -91,7 +109,7 @@ export default function useList, P extends Record<
};
const nextOptions = merge(defaultOptions, options);
- const tmp = typeof next === 'function' ? next(params) : { ...merge({}, params, next) };
+ const tmp = typeof next === 'function' ? next(params) : (mergeParams(params, next) as P);
setParams(tmp);
if (nextOptions.revalidate) {
From 275047327a764869b5558f29d4d4f696b9cf4729 Mon Sep 17 00:00:00 2001
From: LuckyFBB <976060700@qq.com>
Date: Sat, 10 Oct 2026 17:34:33 +0800
Subject: [PATCH 2/2] fix(uselist): support circular references in mergeParams
---
src/useList/__tests__/useList.test.ts | 18 ++++++++++++++++++
src/useList/index.ts | 9 +++++++--
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/src/useList/__tests__/useList.test.ts b/src/useList/__tests__/useList.test.ts
index f1dd8fce1..8e9bc701f 100644
--- a/src/useList/__tests__/useList.test.ts
+++ b/src/useList/__tests__/useList.test.ts
@@ -127,6 +127,24 @@ describe('Test useList hook', () => {
});
});
+ it('Should preserve circular references when merging object params', () => {
+ const fetcher = jest.fn().mockResolvedValue({ total: 0, data: [] });
+ const previous: Record = { value: 'previous' };
+ const next: Record = { value: 'next' };
+ previous.self = previous;
+ next.self = next;
+ const { result } = renderHook(() =>
+ useList(fetcher, { nested: previous }, { immediate: false })
+ );
+
+ act(() => {
+ result.current.mutate({ nested: next }, { revalidate: false });
+ });
+
+ expect(result.current.params.nested.value).toBe('next');
+ expect(result.current.params.nested.self).toBe(result.current.params.nested);
+ });
+
it('Should support get data with current params', () => {
const fetcher = jest.fn().mockResolvedValue({
total: 1,
diff --git a/src/useList/index.ts b/src/useList/index.ts
index 69dcd7d26..061d0606d 100644
--- a/src/useList/index.ts
+++ b/src/useList/index.ts
@@ -3,16 +3,21 @@ import { isPlainObject, merge } from 'lodash-es';
const mergeParams = (
previous: Record,
- next: Record
+ next: Record,
+ merged = new WeakMap