Skip to content

Commit c191989

Browse files
Fix React Parameters Update (#678)
1 parent d74797f commit c191989

File tree

5 files changed

+979
-474
lines changed

5 files changed

+979
-474
lines changed

.changeset/dull-days-sneeze.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/react': patch
3+
---
4+
5+
Fix regression in useQuery where updating the supplied query parameters would not update the underlaying query.

packages/react/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"jsdom": "^24.0.0",
4545
"react": "18.3.1",
4646
"react-dom": "18.3.1",
47-
"react-error-boundary": "^4.1.0"
47+
"react-error-boundary": "^4.1.0",
48+
"@powersync/drizzle-driver": "workspace:*"
4849
}
4950
}

packages/react/src/hooks/watched/watch-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export const constructCompatibleQuery = <RowType>(
6868
execute: () => query.execute()
6969
};
7070
}
71-
}, [query, powerSync]);
71+
}, [query, powerSync, ...parameters]);
7272

7373
const queryChanged = checkQueryChanged(parsedQuery, options);
7474

packages/react/tests/useQuery.test.tsx

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import * as commonSdk from '@powersync/common';
2+
import { toCompilableQuery, wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver';
23
import { PowerSyncDatabase } from '@powersync/web';
34
import { act, cleanup, renderHook, waitFor } from '@testing-library/react';
5+
import { eq } from 'drizzle-orm';
6+
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
47
import pDefer from 'p-defer';
5-
import React from 'react';
8+
import React, { useEffect } from 'react';
69
import { beforeEach, describe, expect, it, onTestFinished, vi } from 'vitest';
710
import { PowerSyncContext } from '../src/hooks/PowerSyncContext';
811
import { useQuery } from '../src/hooks/watched/useQuery';
@@ -168,6 +171,178 @@ describe('useQuery', () => {
168171
);
169172
});
170173

174+
it('should react to updated queries (Explicit Drizzle DB)', async () => {
175+
const db = openPowerSync();
176+
177+
const lists = sqliteTable('lists', {
178+
id: text('id'),
179+
name: text('name')
180+
});
181+
182+
const drizzleDb = wrapPowerSyncWithDrizzle(db, {
183+
schema: {
184+
lists
185+
}
186+
});
187+
188+
const wrapper = ({ children }) => <PowerSyncContext.Provider value={db}>{children}</PowerSyncContext.Provider>;
189+
190+
let updateParameters = (params: string): void => {};
191+
const newParametersPromise = new Promise<string>((resolve) => {
192+
updateParameters = resolve;
193+
});
194+
195+
await db.execute(/* sql */ `
196+
INSERT INTO
197+
lists (id, name)
198+
VALUES
199+
(uuid (), 'first'),
200+
(uuid (), 'second')
201+
`);
202+
203+
const query = () => {
204+
const [name, setName] = React.useState<string>('first');
205+
const drizzleQuery = drizzleDb.select().from(lists).where(eq(lists.name, name));
206+
207+
useEffect(() => {
208+
// allow updating the parameters externally
209+
newParametersPromise.then((params) => setName(params));
210+
}, []);
211+
212+
return useQuery(toCompilableQuery(drizzleQuery));
213+
};
214+
215+
const { result } = renderHook(query, { wrapper });
216+
217+
// We should only receive the first list due to the WHERE clause
218+
await vi.waitFor(
219+
() => {
220+
expect(result.current.data[0]?.name).toEqual('first');
221+
},
222+
{ timeout: 500, interval: 100 }
223+
);
224+
225+
// Now update the parameter
226+
updateParameters('second');
227+
228+
// We should now only receive the second list due to the WHERE clause and updated parameter
229+
await vi.waitFor(
230+
() => {
231+
expect(result.current.data[0]?.name).toEqual('second');
232+
},
233+
{ timeout: 500, interval: 100 }
234+
);
235+
});
236+
237+
it('should react to updated queries', async () => {
238+
const db = openPowerSync();
239+
240+
const wrapper = ({ children }) => <PowerSyncContext.Provider value={db}>{children}</PowerSyncContext.Provider>;
241+
242+
let updateParameters = (params: string[]): void => {};
243+
const newParametersPromise = new Promise<string[]>((resolve) => {
244+
updateParameters = resolve;
245+
});
246+
247+
await db.execute(/* sql */ `
248+
INSERT INTO
249+
lists (id, name)
250+
VALUES
251+
(uuid (), 'first'),
252+
(uuid (), 'second')
253+
`);
254+
255+
const query = () => {
256+
const [parameters, setParameters] = React.useState<string[]>(['first']);
257+
258+
useEffect(() => {
259+
// allow updating the parameters externally
260+
newParametersPromise.then((params) => setParameters(params));
261+
}, []);
262+
263+
const query = React.useMemo(() => {
264+
return {
265+
execute: () => db.getAll<{ name: string }>('SELECT * FROM lists WHERE name = ?', parameters),
266+
compile: () => ({ sql: 'SELECT * FROM lists WHERE name = ?', parameters })
267+
};
268+
}, [parameters]);
269+
270+
return useQuery(query);
271+
};
272+
273+
const { result } = renderHook(query, { wrapper });
274+
275+
// We should only receive the first list due to the WHERE clause
276+
await vi.waitFor(
277+
() => {
278+
expect(result.current.data[0]?.name).toEqual('first');
279+
},
280+
{ timeout: 500, interval: 100 }
281+
);
282+
283+
// Now update the parameter
284+
updateParameters(['second']);
285+
286+
// We should now only receive the second list due to the WHERE clause and updated parameter
287+
await vi.waitFor(
288+
() => {
289+
expect(result.current.data[0]?.name).toEqual('second');
290+
},
291+
{ timeout: 500, interval: 100 }
292+
);
293+
});
294+
295+
it('should react to updated queries (simple update)', async () => {
296+
const db = openPowerSync();
297+
298+
const wrapper = ({ children }) => <PowerSyncContext.Provider value={db}>{children}</PowerSyncContext.Provider>;
299+
300+
let updateParameters = (params: string[]): void => {};
301+
const newParametersPromise = new Promise<string[]>((resolve) => {
302+
updateParameters = resolve;
303+
});
304+
305+
await db.execute(/* sql */ `
306+
INSERT INTO
307+
lists (id, name)
308+
VALUES
309+
(uuid (), 'first'),
310+
(uuid (), 'second')
311+
`);
312+
313+
const query = () => {
314+
const [parameters, setParameters] = React.useState<string[]>(['first']);
315+
316+
useEffect(() => {
317+
// allow updating the parameters externally
318+
newParametersPromise.then((params) => setParameters(params));
319+
}, []);
320+
321+
return useQuery('SELECT * FROM lists WHERE name = ?', parameters);
322+
};
323+
324+
const { result } = renderHook(query, { wrapper });
325+
326+
// We should only receive the first list due to the WHERE clause
327+
await vi.waitFor(
328+
() => {
329+
expect(result.current.data[0]?.name).toEqual('first');
330+
},
331+
{ timeout: 500, interval: 100 }
332+
);
333+
334+
// Now update the parameter
335+
updateParameters(['second']);
336+
337+
// We should now only receive the second list due to the WHERE clause and updated parameter
338+
await vi.waitFor(
339+
() => {
340+
expect(result.current.data[0]?.name).toEqual('second');
341+
},
342+
{ timeout: 500, interval: 100 }
343+
);
344+
});
345+
171346
it('should show an error if parsing the query results in an error', async () => {
172347
const db = openPowerSync();
173348

0 commit comments

Comments
 (0)