Skip to content

useQuery 在文档示例用法下是无界请求循环:内联 options 300ms 内发出 8830 次 find #4693

Closed

Description

@os-zhuang

做 #4682(给 client-react 建测试基座)时,用刚建好的基座实测发现,unassigned。

现象

useQuery(object, options) 在 options 里带任何对象/数组/函数字段 时会进入无界重取循环——而这正是它自己 TSDoc 示例的写法。

实测(jsdom + @testing-library/react,fake client 记 data.find 调用次数,渲染后静置 300ms):

用法 300ms 内 find 调用次数
useQuery('todo_task', { where: { status: 'open' } }) —— 内联 options 8830
const OPTS = {...} 提升到组件外,useQuery('todo_task', OPTS) 1

不是「多取几次」,是以事件循环的速度永远打下去。

成因

packages/client-react/src/data-hooks.tsx:

// L116
const fetchData = useCallback(async (isRefetch = false) => { … },
  [client, object, query, resolvedFields, resolvedWhere, resolvedSort,
   resolvedLimit, resolvedOffset, enabled, onSuccess, onError]);

// L156
useEffect(() => { fetchData(); }, [fetchData]);

resolvedWhere / resolvedFields / resolvedSort 直接来自 options 的解构,onSuccess / onError 同理。调用方内联传入 → 每次渲染都是新身份 → fetchData 每渲染换身份 → L156 的 effect 每渲染重跑 → setData → 重渲染 → 闭环,无出口。

useQuery('todo_task', {})(全部字段 undefined,都是原始值)不受影响——所以它一直没被发现。触发条件是传 where / fields / orderBy / onSuccess / onError 中的任意一个,也就是绝大多数真实用法。

同一形状的 useCallback 依赖链在 data-hooks.tsx:592 和 metadata-hooks.tsx:136 也存在,需要一并核查。

影响

处置方向

不要用「让调用方自己 useMemo」当修复——文档示例就是内联写法,把正确性押在调用方的记忆力上等于没修。

按值而非按身份做依赖。可选:对 where/fields/orderBy 做稳定序列化(如 JSON.stringify 或结构化哈希)作为依赖;onSuccess/onError 存进 ref(它们是副作用回调,本就不该参与重取决策)。

修复必须带回归测试,基座已经就位(#4682 / PR #4692),断言直接用上表那两个数字的形状(内联 options 稳定后 find 只该被调用 1 次)。

复现

基座合入后,packages/client-react/ 下:

const find = vi.fn(async () => ({ data: [], total: 0 }));
const client = { data: { find, query: find }, events: {}, setLocale: vi.fn() } as unknown as ObjectStackClient;
function C() { useQuery('todo_task', { where: { status: 'open' } }); return null; }
render(<ObjectStackProvider client={client}><C /></ObjectStackProvider>);
await new Promise(r => setTimeout(r, 300));
expect(find.mock.calls.length).toBe(1);  // 实测 8830

Activity

  1. self-assigned this
    on Aug 2, 2026
  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    认领开工。维护者批准与 #4694 一并修(同源:依赖数组按身份而非按值)。

    依赖 #4682 的测试基座(PR #4692)才能带回归测试,所以本分支基于该分支起,待其落地后并入 main。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions