Skip to content

fix(tanstack-react-query): use skipToken instead of overriding enabled option#994

Open
msvargas wants to merge 2 commits into
powersync-ja:mainfrom
barvaz-engineering:fix/preserve-user-enabled-option
Open

fix(tanstack-react-query): use skipToken instead of overriding enabled option#994
msvargas wants to merge 2 commits into
powersync-ja:mainfrom
barvaz-engineering:fix/preserve-user-enabled-option

Conversation

@msvargas

Copy link
Copy Markdown
Contributor

Problem

PowerSync's useQuery and useQueries hooks override the user's enabled option by forcing enabled: streamsHaveSynced at the end of the spread. Users who need conditional query control (e.g., enabled: !!userId) lose that ability — the query always runs once streams are synced, regardless of their condition.

Additionally, two secondary issues were discovered:

  1. Stale streamsHaveSynced — The usePowerSyncQueries final useMemo was missing streamsHaveSynced in its dependency array, causing the returned value to stay stale when only streamsHaveSynced changed.

  2. Race condition on first table resolutionresolveTables is async. The change listener attaches watching [] (empty) tables, so any write landing before resolveTables resolves is silently dropped. The listener that later attaches with real tables was created after the write, so it never sees it.

Solution

Replace the enabled override pattern with TanStack's skipToken. When streams haven't synced, the queryFn is set to Tanstack.skipToken, preventing query execution without modifying the user's enabled option.

For useSuspenseQuery, skipToken is not passed (TanStack rejects it for suspense). Suspense queries always receive the real queryFn, which is correct — suspense queries must resolve.

Race condition fix: A tablesInitialized ref tracks each query's [] -> [tables] transition. On the first transition, an explicit queryClient.invalidateQueries() rescues any data that was written and dropped during the pending resolveTables window.

Changes

File Change
src/hooks/useQuery.ts Conditionally apply skipToken when streams haven't synced (and not a suspense query); remove enabled override
src/hooks/useQueries.ts Apply skipToken per-query; remove enabled override; add streamsHaveSynced to deps
src/hooks/usePowerSyncQueries.ts Add tablesInitialized ref with rescue invalidation; add streamsHaveSynced to return memo deps
tests/enabled.test.tsx Verify user enabled: false is respected for useQuery, useQueries, and useSuspenseQuery
tests/usePowerSyncQueries.test.tsx Verify streamsHaveSynced dep update + race condition rescue

Testing

All 41 tests pass (5 test files, 40 passed, 1 pre-existing skip).

@changeset-bot

changeset-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3864737

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@powersync/tanstack-react-query Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@msvargas msvargas force-pushed the fix/preserve-user-enabled-option branch 3 times, most recently from 730b80b to 379fbea Compare June 15, 2026 21:53
@msvargas

msvargas commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @Chriztiaan, could you please review this fix when you have a chance? Feedback is very welcome. Also, thanks for the awesome packages! 🚀

@Chriztiaan Chriztiaan self-requested a review June 22, 2026 09:26
@Chriztiaan

Copy link
Copy Markdown
Contributor

Hey @msvargas, I have a few changes I'd like to push to your branch. Could you give me write permissions to your fork? Alternatively, you could apply the changes yourself - commit here: barvaz-engineering@833436e

@msvargas

Copy link
Copy Markdown
Contributor Author

Thanks for the review @Chriztiaan! I've applied all your suggested changes from commit 833436e locally — they look great. I wasn't able to give you write permissions to the fork, but please feel free to close this PR and open a new one with your changes if you'd prefer to take it from here. Many thanks for the help!

…d option

Replace the enabled: streamsHaveSynced pattern with TanStack's skipToken to preserve the user's own enabled option.

Problems fixed:
- User's enabled option was silently overridden by PowerSync's streamsHaveSynced
- usePowerSyncQueries final useMemo was missing streamsHaveSynced in deps, returning stale values
- Race condition: change listeners attached watching [] tables while resolveTables was pending, losing first-sync writes

Changes:
- useQuery: Conditionally pass skipToken when streams haven't synced; suspense queries always get the real queryFn
- useQueries: Same skipToken approach per query entry; add streamsHaveSynced to deps
- usePowerSyncQueries: Add tablesInitialized ref to rescue data lost during the []->[tables] transition; add streamsHaveSynced to return memo deps
@msvargas msvargas force-pushed the fix/preserve-user-enabled-option branch from 379fbea to 0890392 Compare June 23, 2026 19:49
@Chriztiaan

Copy link
Copy Markdown
Contributor

Thank you for the effort @msvargas, it's greatly appreciated!

Closing this in favour of #1007.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants