[✨] useVisibleTask$ - add IntersectionObserverInit option with 'intersection-observer' strategy #136
Replies: 16 comments
-
Thanks, rootMargin could be a nice addition. I don't understand why, in your Ideal solution, you create the observer inside of useVisibleTask$. rootMargin could be managed by the tasks itself |
Beta Was this translation helpful? Give feedback.
-
@gioboa as far as I understand the intersection observer used by
On step 2 the |
Beta Was this translation helpful? Give feedback.
-
I created this POC https://github.com/gioboa/qwik-infinite-list and it's doing more or less what you described. |
Beta Was this translation helpful? Give feedback.
-
Yes, that's more or less what I'm doing but data comes from the server so it takes more time to fetch. Also, this is one case, but there are many cases when you want to prefetch / precalculate data just a little bit before the element is displayed. |
Beta Was this translation helpful? Give feedback.
-
rootMargin option seems reasonable to me, it's a good first PR to implement. |
Beta Was this translation helpful? Give feedback.
-
I've taken a look at the code and from what I understand it means to add a property alongside
if (eagerness === 'intersection-observer') {
useOn('qvisible', getTaskHandlerQrl(task));
// Set IntersectionObserverInit option on a qvisibleoption property
if (options) {
const invokeCtx = const invokeCtx = useInvokeContext();
invokeCtx.$hostElement$.setAttribute('qvisibleoptions', JSON.stringify(options));
}
}
const results = querySelectorAll('[on\\:qvisible]');
const observerCb = (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
dispatch(entry.target, '', createEvent('qvisible', entry));
}
};
const baseObserver = new IntersectionObserver(observerCb);
results.forEach(el => {
const options = el.getAttribute('qvisibleoptions');
if (!options) return baseObserver.observe(el));
// create new IntersectionObserver if element has options
new IntersectionObserver(observerCb, JSON.parse(options));
}); I understand you don't want to add more code to qwikloader especially to create new IntersectionObserver. |
Beta Was this translation helpful? Give feedback.
-
Hello, before I start the PR, I would like to know if the kind of change I suggest above is acceptable on your side. Thanks |
Beta Was this translation helpful? Give feedback.
-
Thank you @GrandSchtroumpf, for trying to help out! Yes, we would love for you to take this on, and I would love to help you along in the process. Let's go over how this would work. Given: import { component$, useVisibleTask$ } from '@builder.io/qwik';
export default component$(() => {
return (
<div>
<div style={{ height: '100vh' }} />
<Child />
</div>
);
});
export const Child = component$(() => {
useVisibleTask$(
() => {
console.log('task 1');
},
{
strategy: 'intersection-observer',
rootMargin: '100px',
}
);
useVisibleTask$(
() => {
console.log('task 2');
},
{
strategy: 'intersection-observer',
rootMargin: '50px',
}
);
return <div>Hello Qwik!</div>;
}); Notice that we have two <div>
<div style="height:100vh"></div>
<div on:qvisible="chunkA.js#_hW[0] chunkB.js#_hW[1]">
Hello Qwik!
</div>
</div>
So we need to do something special for the above to deal with this correctly.... Here is my proposal: <div>
<div style="height:100vh"></div>
<div on:qvisible
q:visible-options="[{rootMargin: '50px'}, {rootMargin: '100px'}]"
on:qvisible:0="chunkA.js#_hW[0]"
on:qvisible:1="chunkB.js#_hW[1]"
>
Hello Qwik!
</div>
</div>
Not saying that my solution is the best, I am open to other suggestions, but I do think we need to solve the above corner case correctly. Would love to hear your thoughts. Or if you agree feel free to start implementing. NOTE: this is tricky so we will have to make sure to have tests for this case. (I can help with that if you get stuck) NOTE: The other thing to keep in mind is that as of right now this will only work for the initial render. Once the app is running in the client import { component$, useStore, useVisibleTask$ } from '@builder.io/qwik';
export default component$(() => {
const count = useStore([0]);
return (
<div>
<button onClick$={() => count.push(count.length)}>+1</button>
<div style={{ height: '100vh' }} />
{count.map((idx) => (
<Child key={idx} idx={idx} />
))}
</div>
);
});
export const Child = component$<{ idx: number }>(({ idx }) => {
useVisibleTask$(() => {
console.log('task idx', idx);
});
return <div>Idx {idx}!</div>;
}); The output will be blank, but when you click on the button you will see:
And then when you scroll into the view
Basically the We can have a sepearet discussion about that. |
Beta Was this translation helpful? Give feedback.
-
Thanks @mhevery for the detailed feedback. <div>
<div style="height:100vh"></div>
<div on:qvisible="chunkA.js#_hW[0] {}, chunkB.js#_hW[1] {rootMargin: '100px', thresholds: [0, 1]}">
Hello Qwik!
</div>
</div> Ideally we could also let the dev set a Concerning the lazy SSR vs eager CSR, if navigation with |
Beta Was this translation helpful? Give feedback.
-
In theory, yes, but the system is not set up to have extra details like that in
I like that idea... |
Beta Was this translation helpful? Give feedback.
-
I think this could be a two-parter, first the strict intersection observing and later the parameters. |
Beta Was this translation helpful? Give feedback.
-
sounds reasonable to me. |
Beta Was this translation helpful? Give feedback.
-
Hello, |
Beta Was this translation helpful? Give feedback.
-
I read all the discussion and the step here are two.
@lord-cosmos we can start the PR for the first step together. You can DM me on discord |
Beta Was this translation helpful? Give feedback.
-
@gioboa thank you!!
|
Beta Was this translation helpful? Give feedback.
-
We moved this issue to |
Beta Was this translation helpful? Give feedback.
-
Is your feature request related to a problem?
I'm using
useVisibleTask$
to attach an IntersectionObserver on an element in order to load more item when it's 300px from the viewport :The main issue here is that the first operation happens when the ref is visible which creates a jump.
Describe the solution you'd like
Ideally I would like to set the
rootMargin
alongside thestrategy
in theuseVisibleTask$
:This could also help prefetching content a little bit before the element is displayed on the page.
Describe alternatives you've considered
Current I use an eager strategy and ignore first callback from my IntersectionObserver.
Additional context
No response
Beta Was this translation helpful? Give feedback.
All reactions