I'm working on an app where we have transitions between pages that we want to delay if the next page has any lazy-loaded ponents that haven't been loaded yet. So I'm trying to figure out if there's any way to reliably check whether a lazy-loaded ponent has finished loading yet.
This solution works, but only the first time the lazy-loaded ponent tries to load -- i.e. not if it renders instantly because the lazy-loaded ponent is already loaded.
import React, {PropsWithChildren, useEffect} from 'react'
export default function SuspenseTrigger(props) {
return (
<React.Suspense fallback={
<>
{props.fallback}
<Trigger onLoad={props.onLoad} onComplete={props.onComplete} />
</>
}>
{props.children}
</React.Suspense>
)
}
function Trigger(props) {
useEffect(() => {
if (props.onLoad) {
props.onLoad()
}
return () => {
if (props.onComplete) {
setTimeout(props.onComplete)
}
}
}, [])
return <></>
}
This ponent correctly calls onLoad
and onComplete
the first time it's loaded. However, on subsequent times, because the lazy-loaded ponent is now cached, the children are rendered instantly and the fallback is never rendered, which means onLoad
and onComplete
never get called.
One thing I've tried is putting a second Trigger
inside the body of the SuspenseTrigger
:
function ensureLoadCompleteCalled() {
onLoad()
onComplete()
}
return (
<React.Suspense fallback={/* same as before */}>
{props.children}
<Trigger onLoad={ensureLoadCompleteCalled} />
</React.Suspense>
)
That doesn't work because the children of Suspense
get rendered instantly even when other elements aren't fully loaded. So onLoad
and onComplete
get called instantly, regardless of whether the Suspense is finished loading or not.
To get around that, I've also tried some fancier state-checking (code on PasteBin). The main tough thing there is checking whether the fallback has been rendered which I can't figure out how to reliably do. I've tried waiting 100 ms before checking but even that doesn't work reliably for some reason. Maybe it's possible with useRef
?
Any ideas?
I'm working on an app where we have transitions between pages that we want to delay if the next page has any lazy-loaded ponents that haven't been loaded yet. So I'm trying to figure out if there's any way to reliably check whether a lazy-loaded ponent has finished loading yet.
This solution works, but only the first time the lazy-loaded ponent tries to load -- i.e. not if it renders instantly because the lazy-loaded ponent is already loaded.
import React, {PropsWithChildren, useEffect} from 'react'
export default function SuspenseTrigger(props) {
return (
<React.Suspense fallback={
<>
{props.fallback}
<Trigger onLoad={props.onLoad} onComplete={props.onComplete} />
</>
}>
{props.children}
</React.Suspense>
)
}
function Trigger(props) {
useEffect(() => {
if (props.onLoad) {
props.onLoad()
}
return () => {
if (props.onComplete) {
setTimeout(props.onComplete)
}
}
}, [])
return <></>
}
This ponent correctly calls onLoad
and onComplete
the first time it's loaded. However, on subsequent times, because the lazy-loaded ponent is now cached, the children are rendered instantly and the fallback is never rendered, which means onLoad
and onComplete
never get called.
One thing I've tried is putting a second Trigger
inside the body of the SuspenseTrigger
:
function ensureLoadCompleteCalled() {
onLoad()
onComplete()
}
return (
<React.Suspense fallback={/* same as before */}>
{props.children}
<Trigger onLoad={ensureLoadCompleteCalled} />
</React.Suspense>
)
That doesn't work because the children of Suspense
get rendered instantly even when other elements aren't fully loaded. So onLoad
and onComplete
get called instantly, regardless of whether the Suspense is finished loading or not.
To get around that, I've also tried some fancier state-checking (code on PasteBin). The main tough thing there is checking whether the fallback has been rendered which I can't figure out how to reliably do. I've tried waiting 100 ms before checking but even that doesn't work reliably for some reason. Maybe it's possible with useRef
?
Any ideas?
Share Improve this question asked Jun 24, 2020 at 22:14 JanJan 9657 silver badges19 bronze badges 1-
Could you add a second trigger with the children, like this?
{props.children}<Trigger onLoad={props.onComplete} />
– David784 Commented Jun 24, 2020 at 22:33
2 Answers
Reset to default 2have you try wrapping your {props.children}
on a Loader Functional Component, so on useLayoutEffect yo execute the onComplete function?
Here just a raw idea, but it may work...
const Loader = ({ onLoad, onComplete, children }) => {
if (onLoad) {
onLoad();
}
useLayoutEffect(() => {
if (onComplete) {
onComplete();
}
}, []);
return children;
}
<React.Suspense fallback={/* same as before */}>
<Loader onLoad={onLoad} onComplete={onComplete}>
{props.children}
</Loader>
</React.Suspense>
Would checking _status
work?
const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
See https://github./facebook/react/blob/master/packages/react/src/ReactLazy.js