Closed gureckis closed 4 months ago
The onSlideEnter hook is called multiple times bacause each instance of this component registers its own hook. And there are at least two instances of the component: one in the main page, and one in the QuickOverview panel.
You can use ['slide','presenter'].includes($renderContext.value)
to checked whether the instance of the component is rendered in the main page.
However, I am not sure how requestAnimationFrame
affects this hook.
i fixed some of the code in this component and this looping behavior went away.... it remains a mystery but i don't think it needs fixing as is. it must be something more low level than Slidev. 👻
Another piece of strange behavior I noticed with onSlideEnter
that might be more straightforward -- onSlideEnter
is called sometimes before the onMounted()
lifecycle of components inside the slide (possibly the layout too, but i'm not sure). This means these components can't use this new onSlideEnter
event reliably. This comes up mostly when reloading the slidedeck on a slide number later in the deck (like reloading on slide 5 when components in slide 5 are expecting to process onSlideEnter). the components do not load yet and so they fail to respond to the onSlideEnter event. If the deck loads from slide 1 then there's time for everything to load before navigating to the new slide.
onSlideEnter
is called sometimes before theonMounted()
lifecycle of components inside the slide
The implement of onSlideEnter
is:
export function useIsSlideActive() {
const { $page } = useSlideContext()
const { currentSlideNo } = useNav()
return computed(() => $page.value === currentSlideNo.value)
}
export function onSlideEnter(cb: () => void) {
const isActive = useIsSlideActive()
watchEffect(() => isActive.value && cb())
}
So it is a watchEffect
and the callback can be called before mount. You can use it inside the onMounted
hook to make sure this component has been mounted.
onMounted(() => {
onSlideEnter(() => { ... })
})
If you are using it in a slide layer, a possible workaround is:
import { useIsSlideActive, useSlideContext } from '@slidev/client'
const isActive = useIsSlideActive()
const { $clicksContext } = useSlideContext()
watchEffect(() => {
if (isActive.value && $clicksContext.isMounted) {
....
}
})
amazing!! this is so helpful. i hope to pay my debt for all these questions by sharing a fun theme soon!
I'm curious why would the slide layer need to be treated differently than the
onMounted(() =>{ onSlideEnter(cb) }
thing?
Because onMounted
hook is called when this component is mounted. The slide content may be mounted later that the slide layer. In fact, the second way works both for components used in the slide content and slide layer.
I wrote a Vue component that scrolls a div automatically
onSlideEnter()
. The scrolling makes use ofrequestAnimationFrame
. I've noticed that this component callsonSlideEnter()
repeatedly rather than only once when the slide is entered/loaded. When I comment out the part of theonSlideEnter()
function which uses therequestAnimationFrame
stuff it doesn't seem to triggeronSlideEnter
repeatedly. But even then ,you can see the function it is being called approximately 4 times on slide load:I created a stacklitz example reproducing the problem here: https://stackblitz.com/edit/github-vyvxjm?file=components%2FCreditScroll.vue
Environment