Skip to content

async: move poll to usePoll - #8904

Draft
wmertens wants to merge 10 commits into
mainfrom
utils-lib
Draft

async: move poll to usePoll#8904
wmertens wants to merge 10 commits into
mainfrom
utils-lib

Conversation

@wmertens

@wmertens wmertens commented Aug 2, 2026

Copy link
Copy Markdown
Member

The current ComputedSignal API with poll and expires takes quite a lot of core code while it's fairly rare/doesn't cover all use cases.

Furthermore, there was a disconnect between the expires option in routeLoaders and the cache-control headers that might have been set by middleware.

This PR does a few things:

  • adds a new @qwik.dev/utils package, which contains non-core code that can still access internals and which is tied to the exact core version
  • adds a usePoll hook which can be used to poll a computed signal once it hits the client
  • adds .clear() to computed signals, which clears their value, which will cause Suspense boundaries to show while recalculating
  • adds a cacheControl option to routeLoaders. Set to "immutable" to make it be generated during SSG.
  • refetches all routeLoaders on navigation (except immutable ones), thereby fully relying on the browser cache and cache-control settings
  • breaking removes poll, expires, and allowStale from ComputedSignal and thus also routeLoaders
  • limits link-hover data prefetch to once per navigation
  • moves zod$ into utils

@changeset-bot

changeset-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8db15d0

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

This PR includes changesets to release 6 packages
Name Type
@qwik.dev/core Major
@qwik.dev/router Major
@qwik.dev/utils Major
eslint-plugin-qwik Major
@qwik.dev/react Major
create-qwik Major

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

wmertens and others added 9 commits August 2, 2026 14:18
Polling moves out of the async signal engine: usePoll(signal, expires)
invalidates the signal on an interval that starts on document idle and
resets whenever a new result lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Polling now lives in usePoll (@qwik.dev/utils). Drops the expires/poll/
interval options and accessors, the NO_POLL flag, the expires slot in the
AsyncSignal wire format, and the SSR eager-resume hack for polling signals
(the usePoll visible task resumes on qidle instead).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
allowStale:false made every invalidation clear the value; clear() makes
that a one-shot, caller-controlled operation and drops the
CLEAR_ON_INVALIDATE flag and the loader allowStale plumbing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The browser HTTP cache becomes the single freshness authority for loader
data. cacheControl accepts any cacheControl() value or a function of the
request event (null return skips the header); default is no-cache. SSG
writes per-loader files only for cacheControl: 'immutable' loaders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erender on unchanged data

Every navigation invalidates every loader signal; the browser HTTP cache
(driven by each loader's cacheControl) is the freshness authority. The
TTL fetch cache becomes a per-navigation dedupe map shared between hover
prefetch and the nav's own fetches. Loader responses keep their raw text
so an unchanged payload skips deserialization and returns the previous
object, firing no effects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…utils

Also refresh the core-notes agent guidance and add the utils package to
the monorepo map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cacheControl:'immutable' data cannot change until a rebuild, so the
per-navigation invalidation sweep skips those signals; their tracked URL
inputs still invalidate them when the path or search changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Skip immutable loaders in the eager nav kick-off and in link data
prefetch: if the SSR'd page never reads them client-side, their JSON is
never downloaded. First read fetches once per deploy (immutable
browser caching covers every fetch after that).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

@qwik.dev/core

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/core@8904

@qwik.dev/router

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/router@8904

eslint-plugin-qwik

npm i https://pkg.pr.new/QwikDev/qwik/eslint-plugin-qwik@8904

create-qwik

npm i https://pkg.pr.new/QwikDev/qwik/create-qwik@8904

@qwik.dev/optimizer

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/optimizer@8904

@qwik.dev/devtools

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/devtools@8904

commit: 8db15d0

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor
built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview 8db15d0

* @public
*/
export const usePoll = (signal: ComputedSignal<unknown>, expires: number) => {
useVisibleTask$(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't it leverage useOnDocument instead? Are we sure we want to provide a general hook for this or could providing recipes on how to build one in the docs be more helpful to consumers?

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

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants