0

I lazy load a component with lazy & suspense. It works, but the transition is very raw. Is there a simple way to apply a smooth transition without wrapping each suspended component with a keyframe opacity animation(on each re-render it will trigger again, I don't want this. Only on transition once the component is ready to be displayed)?

base code:

const Foo = lazy(()=> import("./Foo"))

function Component (){
  return (
    <Suspense fallback={<Loader/>}>
     <Foo/>
    </Suspense>
  )
}
DoneDeal0
  • 5,273
  • 13
  • 55
  • 114

1 Answers1

0

I had a similar problem and I solved it by creating a <Lazy ... /> component. Here my solution, maybe it can help you.

import React from "react"
import PleaseWait from "../please-wait"

export default function Lazy<P>(
    props: P & {
        factory: () => Promise<{ default: (props: P) => JSX.Element }>
    }
) {
    const { factory } = props
    const [element, setElement] = React.useState<JSX.Element | null>(null)
    React.useEffect(() => {
        setElement(null)
        factory().then(mod => setElement(mod.default(props)))
    }, [factory])
    return <PleaseWait>{element}</PleaseWait>
}

And here is how I use it:

<Lazy
    factory={() => import("./menu")}
    traceMsg={props.myMessage}
/>

The component I defined in "./menu" needs traceMsg has prop. Once you write factory={() => import("./menu")}, TypeScript is smart enough to let VSCode provie you with intellisense for traceMsg.

As for the component <PleaseWait/>, its up to your needs. In my case, I had this:

export interface PleaseWaitViewProps {
    className?: string
    children?: JSX.Element | JSX.Element[] | string | null
}

export default function PleaseWaitView(props: PleaseWaitViewProps) {
    const { children } = props
    return (
        <div className={getClassNames(props)}>
            <div className={`element ${children ? "show" : "hide"}`}>
                {children}
            </div>
            <div className={`fallback ${children ? "hide" : "show"}`}>
                {/* Whatever spinning animation you want... */}
            </div>
        </div>
    )
}

Of course, this is not a perfect solution, because you end up with two <div> surrounding your component.

Tolokoban
  • 2,297
  • 14
  • 17