diff --git a/.changeset/slick-squids-draw.md b/.changeset/slick-squids-draw.md
new file mode 100644
index 00000000000..344511606aa
--- /dev/null
+++ b/.changeset/slick-squids-draw.md
@@ -0,0 +1,6 @@
+---
+'@builder.io/qwik-city': minor
+'@builder.io/qwik': minor
+---
+
+FEAT: useVisibleTask$ now accepts the strategy "idle-visible", which waits until document idle before running visible tasks. This improves the LCP Web Vitals metric. It typically delays visible tasks for less than a second at document load, and is our recommended setting.
diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json
index ad623491861..eeb1702e398 100644
--- a/packages/docs/src/routes/api/qwik/api.json
+++ b/packages/docs/src/routes/api/qwik/api.json
@@ -1774,7 +1774,7 @@
}
],
"kind": "Function",
- "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n
\n\nParameter\n\n\n | \n\nType\n\n\n | \n\nDescription\n\n\n |
\n\n\nopts\n\n\n | \n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n | \n\n\n |
\n
\n**Returns:**\n\n[JSXNode](#jsxnode)<'script'>",
+ "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n\n\nParameter\n\n\n | \n\nType\n\n\n | \n\nDescription\n\n\n |
\n\n\nopts\n\n\n | \n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n | \n\n\n |
\n
\n**Returns:**\n\nJSXNode<'script'>",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
"mdFile": "qwik.prefetchserviceworker.md"
},
@@ -2058,6 +2058,20 @@
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts",
"mdFile": "qwik.qwikidleevent.md"
},
+ {
+ "name": "QwikIdlevisibleEvent",
+ "id": "qwikidlevisibleevent",
+ "hierarchy": [
+ {
+ "name": "QwikIdlevisibleEvent",
+ "id": "qwikidlevisibleevent"
+ }
+ ],
+ "kind": "TypeAlias",
+ "content": "Emitted by qwik-loader when an element becomes visible, after document idle. Used by `useVisibleTask$`\n\n\n```typescript\nexport type QwikIdlevisibleEvent = CustomEvent;\n```",
+ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts",
+ "mdFile": "qwik.qwikidlevisibleevent.md"
+ },
{
"name": "QwikInitEvent",
"id": "qwikinitevent",
@@ -3454,7 +3468,7 @@
}
],
"kind": "TypeAlias",
- "content": "```typescript\nexport type VisibleTaskStrategy = 'intersection-observer' | 'document-ready' | 'document-idle';\n```",
+ "content": "```typescript\nexport type VisibleTaskStrategy = 'intersection-observer' | 'document-ready' | 'document-idle' | 'idle-visible';\n```",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts",
"mdFile": "qwik.visibletaskstrategy.md"
},
diff --git a/packages/docs/src/routes/api/qwik/index.mdx b/packages/docs/src/routes/api/qwik/index.mdx
index 8414e2e4b8b..34157761a35 100644
--- a/packages/docs/src/routes/api/qwik/index.mdx
+++ b/packages/docs/src/routes/api/qwik/index.mdx
@@ -3651,7 +3651,7 @@ opts
**Returns:**
-[JSXNode](#jsxnode)<'script'>
+JSXNode<'script'>
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
@@ -4186,6 +4186,16 @@ export type QwikIdleEvent = CustomEvent<{}>;
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts)
+## QwikIdlevisibleEvent
+
+Emitted by qwik-loader when an element becomes visible, after document idle. Used by `useVisibleTask$`
+
+```typescript
+export type QwikIdlevisibleEvent = CustomEvent;
+```
+
+[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts)
+
## QwikInitEvent
Emitted by qwik-loader on document when the document first becomes interactive
@@ -11565,7 +11575,8 @@ export interface VideoHTMLAttributes extends Attrs<'video', T
export type VisibleTaskStrategy =
| "intersection-observer"
| "document-ready"
- | "document-idle";
+ | "document-idle"
+ | "idle-visible";
```
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts)
diff --git a/packages/qwik-city/src/runtime/src/link-component.tsx b/packages/qwik-city/src/runtime/src/link-component.tsx
index 765463fc3fb..818ae9d5f69 100644
--- a/packages/qwik-city/src/runtime/src/link-component.tsx
+++ b/packages/qwik-city/src/runtime/src/link-component.tsx
@@ -83,13 +83,16 @@ export const Link = component$((props) => {
})
: undefined;
- useVisibleTask$(({ track }) => {
- track(() => loc.url.pathname);
- // Don't prefetch on visible in dev mode
- if (!isDev && anchorRef.value) {
- handlePrefetch?.(undefined, anchorRef.value!);
- }
- });
+ useVisibleTask$(
+ ({ track }) => {
+ track(() => loc.url.pathname);
+ // Don't prefetch on visible in dev mode
+ if (!isDev && anchorRef.value) {
+ handlePrefetch?.(undefined, anchorRef.value!);
+ }
+ },
+ { strategy: 'idle-visible' }
+ );
return (
{
registrations.forEach((registration) => {
if (registration.active) {
- if (registration.active.scriptURL.endsWith('URL')) {
+ if (registration.active.scriptURL.endsWith('_URL_')) {
registration.unregister().catch(console.error);
}
}
diff --git a/packages/qwik/src/core/index.ts b/packages/qwik/src/core/index.ts
index 7fb3c218639..df82787ff73 100644
--- a/packages/qwik/src/core/index.ts
+++ b/packages/qwik/src/core/index.ts
@@ -134,9 +134,10 @@ export { version } from './version';
// Qwik Events
//////////////////////////////////////////////////////////////////////////////////////////
export type {
- KnownEventNames as KnownEventNames,
+ KnownEventNames,
QwikSymbolEvent,
QwikVisibleEvent,
+ QwikIdlevisibleEvent,
QwikIdleEvent,
QwikInitEvent,
QwikTransitionEvent,
diff --git a/packages/qwik/src/core/qwik.core.api.md b/packages/qwik/src/core/qwik.core.api.md
index 2c23e633964..1f4590b2df3 100644
--- a/packages/qwik/src/core/qwik.core.api.md
+++ b/packages/qwik/src/core/qwik.core.api.md
@@ -724,6 +724,9 @@ export type QwikHTMLElements = {
// @public
export type QwikIdleEvent = CustomEvent<{}>;
+// @public
+export type QwikIdlevisibleEvent = CustomEvent;
+
// @public
export type QwikInitEvent = CustomEvent<{}>;
@@ -1756,7 +1759,7 @@ export interface VideoHTMLAttributes extends Attrs<'video', T
}
// @public (undocumented)
-export type VisibleTaskStrategy = 'intersection-observer' | 'document-ready' | 'document-idle';
+export type VisibleTaskStrategy = 'intersection-observer' | 'document-ready' | 'document-idle' | 'idle-visible';
// @internal (undocumented)
export const _waitUntilRendered: (elm: Element) => Promise;
diff --git a/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts b/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts
index b713873224c..a8906275151 100644
--- a/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts
+++ b/packages/qwik/src/core/render/jsx/types/jsx-qwik-attributes.ts
@@ -3,6 +3,7 @@ import type { Signal } from '../../../state/signal';
import type { JSXNode } from './jsx-node';
import type {
QwikIdleEvent,
+ QwikIdlevisibleEvent,
QwikInitEvent,
QwikSymbolEvent,
QwikViewTransitionEvent,
@@ -65,6 +66,7 @@ type PascalCaseNames =
| 'PointerOver'
| 'PointerUp'
| 'QIdle'
+ | 'QIdlevisible'
| 'QInit'
| 'QSymbol'
| 'QVisible'
@@ -111,6 +113,7 @@ type AllEventMapRaw = HTMLElementEventMap &
DocumentEventMap &
WindowEventHandlersEventMap & {
qidle: QwikIdleEvent;
+ qidlevisible: QwikIdlevisibleEvent;
qinit: QwikInitEvent;
qsymbol: QwikSymbolEvent;
qvisible: QwikVisibleEvent;
diff --git a/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts b/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts
index a4e020ae00e..8a090a82519 100644
--- a/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts
+++ b/packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts
@@ -2,6 +2,11 @@ import type { AllEventKeys } from './jsx-qwik-attributes';
/** Emitted by qwik-loader when an element becomes visible. Used by `useVisibleTask$` @public */
export type QwikVisibleEvent = CustomEvent;
+/**
+ * Emitted by qwik-loader when an element becomes visible, after document idle. Used by
+ * `useVisibleTask$` @public
+ */
+export type QwikIdlevisibleEvent = CustomEvent;
/** Emitted by qwik-loader when a module was lazily loaded @public */
export type QwikSymbolEvent = CustomEvent<{ symbol: string; element: Element; reqTime: number }>;
/** Emitted by qwik-loader on document when the document first becomes interactive @public */
diff --git a/packages/qwik/src/core/use/use-task.ts b/packages/qwik/src/core/use/use-task.ts
index 85eca6b64c9..9bd54958110 100644
--- a/packages/qwik/src/core/use/use-task.ts
+++ b/packages/qwik/src/core/use/use-task.ts
@@ -190,7 +190,11 @@ export interface DescriptorBase {
export type EagernessOptions = 'visible' | 'load' | 'idle';
/** @public */
-export type VisibleTaskStrategy = 'intersection-observer' | 'document-ready' | 'document-idle';
+export type VisibleTaskStrategy =
+ | 'intersection-observer'
+ | 'document-ready'
+ | 'document-idle'
+ | 'idle-visible';
/** @public */
export interface OnVisibleTaskOptions {
@@ -809,6 +813,8 @@ const useRunTask = (
) => {
if (eagerness === 'visible' || eagerness === 'intersection-observer') {
useOn('qvisible', getTaskHandlerQrl(task));
+ } else if (eagerness === 'idle-visible') {
+ useOn('qidlevisible', getTaskHandlerQrl(task));
} else if (eagerness === 'load' || eagerness === 'document-ready') {
useOnDocument('qinit', getTaskHandlerQrl(task));
} else if (eagerness === 'idle' || eagerness === 'document-idle') {
diff --git a/packages/qwik/src/qwikloader.ts b/packages/qwik/src/qwikloader.ts
index c4886f6950c..f868691d651 100644
--- a/packages/qwik/src/qwikloader.ts
+++ b/packages/qwik/src/qwikloader.ts
@@ -203,6 +203,21 @@ type qWindow = Window & {
broadcast('-window', ev, camelToKebab(ev.type));
};
+ const observeVisible = (type: string) => {
+ const results = querySelectorAll(`[on\\:${type}]`);
+ if (results.length) {
+ const observer = new IntersectionObserver((entries) => {
+ for (const entry of entries) {
+ if (entry.isIntersecting) {
+ observer.unobserve(entry.target);
+ dispatch(entry.target, '', createEvent(type, entry));
+ }
+ }
+ });
+ results.forEach((el) => observer.observe(el));
+ }
+ };
+
const processReadyStateChange = () => {
const readyState = doc.readyState;
if (!hasInitialized && (readyState == 'interactive' || readyState == 'complete')) {
@@ -211,21 +226,15 @@ type qWindow = Window & {
hasInitialized = 1;
emitEvent('qinit');
+
const riC = win.requestIdleCallback ?? win.setTimeout;
- riC.bind(win)(() => emitEvent('qidle'));
+ riC.bind(win)(() => {
+ emitEvent('qidle');
- if (events.has('qvisible')) {
- const results = querySelectorAll('[on\\:qvisible]');
- const observer = new IntersectionObserver((entries) => {
- for (const entry of entries) {
- if (entry.isIntersecting) {
- observer.unobserve(entry.target);
- dispatch(entry.target, '', createEvent('qvisible', entry));
- }
- }
- });
- results.forEach((el) => observer.observe(el));
- }
+ observeVisible('qidlevisible');
+ });
+
+ observeVisible('qvisible');
}
};
diff --git a/packages/qwik/src/qwikloader.unit.ts b/packages/qwik/src/qwikloader.unit.ts
index 99051350611..096fbf8aef7 100644
--- a/packages/qwik/src/qwikloader.unit.ts
+++ b/packages/qwik/src/qwikloader.unit.ts
@@ -21,9 +21,9 @@ test('qwikloader script', () => {
* dereference objects etc, but that actually results in worse compression
*/
const compressed = compress(Buffer.from(qwikLoader), { mode: 1, quality: 11 });
- expect(compressed.length).toBe(1428);
- expect(qwikLoader.length).toBe(3126);
+ expect(compressed.length).toBe(1444);
+ expect(qwikLoader.length).toBe(3148);
expect(qwikLoader).toMatchInlineSnapshot(
- `"(()=>{const t=document,e=window,n=new Set,o=new Set([t]);let r;const s=(t,e)=>Array.from(t.querySelectorAll(e)),a=t=>{const e=[];return o.forEach((n=>e.push(...s(n,t)))),e},i=t=>{w(t),s(t,"[q\\\\:shadowroot]").forEach((t=>{const e=t.shadowRoot;e&&i(e)}))},c=t=>t&&"function"==typeof t.then,l=(t,e,n=e.type)=>{a("[on"+t+"\\\\:"+n+"]").forEach((o=>b(o,t,e,n)))},f=e=>{if(void 0===e._qwikjson_){let n=(e===t.documentElement?t.body:e).lastElementChild;for(;n;){if("SCRIPT"===n.tagName&&"qwik/json"===n.getAttribute("type")){e._qwikjson_=JSON.parse(n.textContent.replace(/\\\\x3C(\\/?script)/gi,"<$1"));break}n=n.previousElementSibling}}},p=(t,e)=>new CustomEvent(t,{detail:e}),b=async(e,n,o,r=o.type)=>{const s="on"+n+":"+r;e.hasAttribute("preventdefault:"+r)&&o.preventDefault(),e.hasAttribute("stoppropagation:"+r)&&o.stopPropagation();const a=e._qc_,i=a&&a.li.filter((t=>t[0]===s));if(i&&i.length>0){for(const t of i){const n=t[1].getFn([e,o],(()=>e.isConnected))(o,e),r=o.cancelBubble;c(n)&&await n,r&&o.stopPropagation()}return}const l=e.getAttribute(s);if(l){const n=e.closest("[q\\\\:container]"),r=n.getAttribute("q:base"),s=n.getAttribute("q:version")||"unknown",a=n.getAttribute("q:manifest-hash")||"dev",i=new URL(r,t.baseURI);for(const p of l.split("\\n")){const l=new URL(p,i),b=l.href,h=l.hash.replace(/^#?([^?[|]*).*$/,"$1")||"default",q=performance.now();let _,d,y;const w=p.startsWith("#"),g={qBase:r,qManifest:a,qVersion:s,href:b,symbol:h,element:e,reqTime:q};if(w){const e=n.getAttribute("q:instance");_=(t["qFuncs_"+e]||[])[Number.parseInt(h)],_||(d="sync",y=Error("sym:"+h))}else{const t=l.href.split("#")[0];try{const e=import(t);f(n),_=(await e)[h],_||(d="no-symbol",y=Error(\`\${h} not in \${t}\`))}catch(t){d||(d="async"),y=t}}if(!_){u("qerror",{importError:d,error:y,...g}),console.error(y);break}const m=t.__q_context__;if(e.isConnected)try{t.__q_context__=[e,o,l],w||u("qsymbol",{...g});const n=_(o,e);c(n)&&await n}catch(t){u("qerror",{error:t,...g})}finally{t.__q_context__=m}}}},u=(e,n)=>{t.dispatchEvent(p(e,n))},h=t=>t.replace(/([A-Z])/g,(t=>"-"+t.toLowerCase())),q=async t=>{let e=h(t.type),n=t.target;for(l("-document",t,e);n&&n.getAttribute;){const o=b(n,"",t,e);let r=t.cancelBubble;c(o)&&await o,r=r||t.cancelBubble||n.hasAttribute("stoppropagation:"+t.type),n=t.bubbles&&!0!==r?n.parentElement:null}},_=t=>{l("-window",t,h(t.type))},d=()=>{var s;const c=t.readyState;if(!r&&("interactive"==c||"complete"==c)&&(o.forEach(i),r=1,u("qinit"),(null!=(s=e.requestIdleCallback)?s:e.setTimeout).bind(e)((()=>u("qidle"))),n.has("qvisible"))){const t=a("[on\\\\:qvisible]"),e=new IntersectionObserver((t=>{for(const n of t)n.isIntersecting&&(e.unobserve(n.target),b(n.target,"",p("qvisible",n)))}));t.forEach((t=>e.observe(t)))}},y=(t,e,n,o=!1)=>t.addEventListener(e,n,{capture:o,passive:!1}),w=(...t)=>{for(const r of t)"string"==typeof r?n.has(r)||(o.forEach((t=>y(t,r,q,!0))),y(e,r,_,!0),n.add(r)):o.has(r)||(n.forEach((t=>y(r,t,q,!0))),o.add(r))};if(!("__q_context__"in t)){t.__q_context__=0;const r=e.qwikevents;Array.isArray(r)&&w(...r),e.qwikevents={events:n,roots:o,push:w},y(t,"readystatechange",d),d()}})();"`
+ `"(()=>{const t=document,e=window,n=new Set,o=new Set([t]);let r;const s=(t,e)=>Array.from(t.querySelectorAll(e)),a=t=>{const e=[];return o.forEach((n=>e.push(...s(n,t)))),e},i=t=>{w(t),s(t,"[q\\\\:shadowroot]").forEach((t=>{const e=t.shadowRoot;e&&i(e)}))},c=t=>t&&"function"==typeof t.then,l=(t,e,n=e.type)=>{a("[on"+t+"\\\\:"+n+"]").forEach((o=>u(o,t,e,n)))},f=e=>{if(void 0===e._qwikjson_){let n=(e===t.documentElement?t.body:e).lastElementChild;for(;n;){if("SCRIPT"===n.tagName&&"qwik/json"===n.getAttribute("type")){e._qwikjson_=JSON.parse(n.textContent.replace(/\\\\x3C(\\/?script)/gi,"<$1"));break}n=n.previousElementSibling}}},p=(t,e)=>new CustomEvent(t,{detail:e}),u=async(e,n,o,r=o.type)=>{const s="on"+n+":"+r;e.hasAttribute("preventdefault:"+r)&&o.preventDefault(),e.hasAttribute("stoppropagation:"+r)&&o.stopPropagation();const a=e._qc_,i=a&&a.li.filter((t=>t[0]===s));if(i&&i.length>0){for(const t of i){const n=t[1].getFn([e,o],(()=>e.isConnected))(o,e),r=o.cancelBubble;c(n)&&await n,r&&o.stopPropagation()}return}const l=e.getAttribute(s);if(l){const n=e.closest("[q\\\\:container]"),r=n.getAttribute("q:base"),s=n.getAttribute("q:version")||"unknown",a=n.getAttribute("q:manifest-hash")||"dev",i=new URL(r,t.baseURI);for(const p of l.split("\\n")){const l=new URL(p,i),u=l.href,h=l.hash.replace(/^#?([^?[|]*).*$/,"$1")||"default",_=performance.now();let q,d,y;const g=p.startsWith("#"),w={qBase:r,qManifest:a,qVersion:s,href:u,symbol:h,element:e,reqTime:_};if(g){const e=n.getAttribute("q:instance");q=(t["qFuncs_"+e]||[])[Number.parseInt(h)],q||(d="sync",y=Error("sym:"+h))}else{const t=l.href.split("#")[0];try{const e=import(t);f(n),q=(await e)[h],q||(d="no-symbol",y=Error(\`\${h} not in \${t}\`))}catch(t){d||(d="async"),y=t}}if(!q){b("qerror",{importError:d,error:y,...w}),console.error(y);break}const m=t.__q_context__;if(e.isConnected)try{t.__q_context__=[e,o,l],g||b("qsymbol",{...w});const n=q(o,e);c(n)&&await n}catch(t){b("qerror",{error:t,...w})}finally{t.__q_context__=m}}}},b=(e,n)=>{t.dispatchEvent(p(e,n))},h=t=>t.replace(/([A-Z])/g,(t=>"-"+t.toLowerCase())),_=async t=>{let e=h(t.type),n=t.target;for(l("-document",t,e);n&&n.getAttribute;){const o=u(n,"",t,e);let r=t.cancelBubble;c(o)&&await o,r=r||t.cancelBubble||n.hasAttribute("stoppropagation:"+t.type),n=t.bubbles&&!0!==r?n.parentElement:null}},q=t=>{l("-window",t,h(t.type))},d=t=>{const e=a(\`[on\\\\:\${t}]\`);if(e.length){const n=new IntersectionObserver((e=>{for(const o of e)o.isIntersecting&&(n.unobserve(o.target),u(o.target,"",p(t,o)))}));e.forEach((t=>n.observe(t)))}},y=()=>{var n;const s=t.readyState;r||"interactive"!=s&&"complete"!=s||(o.forEach(i),r=1,b("qinit"),(null!=(n=e.requestIdleCallback)?n:e.setTimeout).bind(e)((()=>{b("qidle"),d("qidlevisible")})),d("qvisible"))},g=(t,e,n,o=!1)=>t.addEventListener(e,n,{capture:o,passive:!1}),w=(...t)=>{for(const r of t)"string"==typeof r?n.has(r)||(o.forEach((t=>g(t,r,_,!0))),g(e,r,q,!0),n.add(r)):o.has(r)||(n.forEach((t=>g(r,t,_,!0))),o.add(r))};if(!("__q_context__"in t)){t.__q_context__=0;const r=e.qwikevents;Array.isArray(r)&&w(...r),e.qwikevents={events:n,roots:o,push:w},g(t,"readystatechange",y),y()}})();"`
);
});
diff --git a/starters/apps/e2e/src/components/effect-client/effect-client.tsx b/starters/apps/e2e/src/components/effect-client/effect-client.tsx
index f5386352cd6..661a862de08 100644
--- a/starters/apps/e2e/src/components/effect-client/effect-client.tsx
+++ b/starters/apps/e2e/src/components/effect-client/effect-client.tsx
@@ -114,6 +114,7 @@ export const ClientSide = component$(() => {
text1: "empty 1",
text2: "empty 2",
text3: "empty 3",
+ text4: "empty 4",
});
useVisibleTask$(
@@ -138,11 +139,21 @@ export const ClientSide = component$(() => {
},
);
+ useVisibleTask$(
+ () => {
+ state.text4 = "run";
+ },
+ {
+ strategy: "idle-visible",
+ },
+ );
+
return (
<>
{state.text1}
{state.text2}
{state.text3}
+ {state.text4}
>
);
});
diff --git a/starters/e2e/e2e.effect-client.spec.ts b/starters/e2e/e2e.effect-client.spec.ts
index 8addef302cd..20fe20734e1 100644
--- a/starters/e2e/e2e.effect-client.spec.ts
+++ b/starters/e2e/e2e.effect-client.spec.ts
@@ -19,6 +19,7 @@ test.describe("effect-client", () => {
const msgClientSide1 = page.locator("#client-side-msg-1");
const msgClientSide2 = page.locator("#client-side-msg-2");
const msgClientSide3 = page.locator("#client-side-msg-3");
+ const msgClientSide4 = page.locator("#client-side-msg-4");
await expect(container).not.hasAttribute("data-effect");
await expect(counter).toHaveText("0");
@@ -27,7 +28,7 @@ test.describe("effect-client", () => {
await expect(msgClientSide1).toHaveText("run");
await expect(msgClientSide2).toHaveText("run");
await expect(msgClientSide3).toHaveText("run");
-
+ await expect(msgClientSide4).toHaveText("run");
await counter.scrollIntoViewIfNeeded();
await expect(container).toHaveAttribute("data-effect", "true");