Closed samselikoff closed 5 months ago
I'd also be really interested in any solutions to this. I'm trying to figure out how to do page transitions, but wait for the sections of the page which are code-split.
@samselikoff @mattgperry on a related note:
Could it be possible to coordinate Suspense
with AnimatePresence
in such a way as to avoid rendering the fallback
—or in other words, wait for the suspended component to resolve? I'm thinking of a page-transition scenario where AnimatePresence
is wrapped around a Page
component that contains async items (think loadable-components,
or next/dynamic
).
Naïve example: here I'd like to get AnimatePresence
to wait until Suspense
resolves the Page
, not transition from Page
to null
to Page
(assume also that Page
returns a motion component)
<AnimatePresence exitBeforeEnter>
<Suspense fallback={null}>
<Page {...pageProps} key={router.asPath}/>
</Suspense>
</AnimatePresence>
I guess I now understand why my exit animations for my fallback component were not working
Hello, I think I have the same problem. I am trying to use AnimatePresence in order to make some route navigation transitions using NextJs 13. NextJs 13 is now using Suspense and my AnimatePresence parent will wrap the NextJs tree: (my AnimatePresence is in the layout.tsx).
In my case, the component (page) that is about to be unmounted is not "frozen". The children (the new page) updates in both components. The old page (that is about to be unmounted) should be completely frozen.
Next13 Docs: https://beta.nextjs.org/docs/routing/fundamentals Any updates on this one?
@dumbravaandrei22 Im also experiencing this problem. Did you find any solution or workaround to this?
hey @samypogs Yes, I did.
I used react-freeze and usePresence order to accomplish this. My code under the AnimatePresence looks like this:
But this is just a workaround. AnimatePresence should know how to handle this case. So the GitHub issue should stay open.
Also interested in achieving this! 🙋
Would be lovely to be able to natively do that :)
This would be amazing!
Error still occurs. I tried dumbravaandrei22 solution but still fails.
@UPDATE My issue was a bit unrelated. But if someone ever encounter is. Key is to check if component is mounted before do any framer motion action
The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.
Main Component
<AnimatePresence>
{isOpen && (
<Child animation={animation} classes={classes} key="something">
{children}
</Child>
)}
</AnimatePresence>
);
Child Component
const [isPresent, safeToRemove] = usePresence();
return (
<motion.div
{...animation}
transition={{ duration: 0.2 }} // Duration of the animation (in seconds)
className={classes}
onAnimationComplete={() => {
if (!isPresent) safeToRemove();
}}
>
<Freeze freeze={!isPresent}>
<div>{children}</div>
</Freeze>
</motion.div>
);
};```
@dumbravaandrei22 and @tomaszbryndzia -- could you create an example of your workaround in action?
does it replace using < Suspense >?
I'm a newbie, faced the same problem and tried to solve it like this. The FullscreenLoader component appears suddenly to hide the content of an unloaded page, but disappears smoothly.
type FallbackProps = {
onMount: () => void
onUnmount: () => void
}
const Fallback: React.FunctionComponent<FallbackProps> = (props) => {
React.useEffect(() => {
props.onMount()
return () => {
setTimeout(props.onUnmount, 1000)
}
}, [])
return null
}
const PageManager: React.FunctionComponent = () => {
const [ isLoading, setIsLoading ] = React.useState<boolean>(true)
function startLoading(): void {
setIsLoading(true)
}
function endLoading(): void {
setIsLoading(false)
}
useRedirectToInitialPage()
const {
actualRoutes,
redirectPath,
redirectFrom
} = useAppRoutingInformation()
return (
<React.Fragment>
<FullscreenLoader
isVisible={isLoading}
message="Загружаем необходимые данные, пожалуйста, подождите."
/>
<AnimatePresence initial={false} mode="wait">
<React.Suspense fallback={(
<Fallback
onMount={startLoading}
onUnmount={endLoading}
/>
)}>
<Routes>
<Route Component={Wrapper}>
{actualRoutes.map((route) => (
<Route
key={route.path}
path={route.path}
Component={route.Component}
/>
))}
<Route
path="*"
element={ <Navigate to={redirectPath} state={{from: redirectFrom}} /> }
/>
</Route>
</Routes>
</React.Suspense>
</AnimatePresence>
</React.Fragment>
)
}
const FullscreenLoader: React.FunctionComponent<Props> = (props) => {
return (
<AnimatePresence initial={false}>
{props.isVisible && (
<LoaderBackdrop>
<LoaderContent message={props.message} />
</LoaderBackdrop>
)}
</AnimatePresence>
)
}
I have here a working example using context: codesandbox
Closing this as a wontfix
I just had a spike on this. My general approach, if anyone wants to take a stab, is to have a component with the same sig as Suspense
i.e
<AnimateSuspense fallback={fallback}>{children}</AnimateSuspense>
This contained a Suspense
component that was provided children
but instead of providing fallback
we provide one that just contains an effect that fires when the component unmounts. This sets some state that removes the user-provided fallback
which we've been provided by the user.
However it didn't work too well, the to fundamental issues being how do we conditionally not render fallback
if the children
Promise
doesn't throw? And how do we know if it throws in the future? This makes it ok to animate children when its first mounted but the expectation would be that its exit
prop works too, and I couldn't get that working either.
It looks like Suspense currently doesn't support unmount animations for its Fallback component. There are a few related discussions/RFCs in the React repo like this one.
Curious if there are currently any known/good workarounds for this? I've been toying with something like this example from a SO answer where I force a data-loading component to continue suspending until after a completes a fadeOut animation using a MotionValue and
animate()
function, but haven't landed on anything decent yet.Just wondering if anyone has any good alternatives for the time being!