7

I'm using the dynamic import() syntax to split each route component into a separate bundle and React.lazy to load the components only when they are required. I have a PageLoading component which serves as the fallback content for <React.Suspense />.

Is it possible to keep the current view component mounted until the "lazy loaded" component is ready to be rendered? The bundles are quite small individually so inevitably the page just ends up flashing for less than half a second while the "fallback" is rendered.

Thank you.

Edit: I am not looking to create an artificial delay. What am I asking for is exactly as I have described.

Ben
  • 91
  • 1
  • 4
  • Possible duplicate of [React suspense/lazy delay?](https://stackoverflow.com/questions/54158994/react-suspense-lazy-delay) – Estus Flask Apr 13 '19 at 22:08
  • While that is a possible solution, I am not looking to create an artificial delay in this case. I really do want the current component to remain mounted until the next component is ready, thereby rendering the need to have a `PageLoading` component unnecessary in this particular case. – Ben Apr 14 '19 at 09:20
  • You mentioned about flashing and that's how it can be addressed with Suspense. What you're describing exactly isn't how Suspense works, it's not possible with it. Changing components like that looks more like a case for a router. I'd say ui-router would be a better choice, see https://ui-router.github.io/guide/lazyloading#react – Estus Flask Apr 14 '19 at 09:45

3 Answers3

5

I wrote a component that accepts the lazy component you want to render and sets the fallback property of <Suspense /> to be the previously rendered component:

import * as React from 'react';

export type LazyProps = {
  component: React.LazyExoticComponent<() => JSX.Element>;
  initialFallback?: JSX.Element;
};

export const Lazy = ({ component, initialFallback = <></> }: LazyProps): JSX.Element => {
  const fallback = React.useRef(() => initialFallback);
  const Component = component;

  const updateFallback = async (): Promise<void> => {
    const result = await component._result;
    fallback.current = typeof result === 'function' ? result : (result as any).default;
  };

  React.useEffect(() => {
    updateFallback();
  }, [component]);

  return (
    <React.Suspense fallback={<fallback.current />}>
      <Component />
    </React.Suspense>
  );
};

Which can be used as such:

<Lazy component={MyLazyComponent} />
5

Do you mean something like this?

I was also searching for the right approach and tried many ways. Currently my best approach is to update the fallback whenever a page is rendered whether that page is lazy-loaded or not. So the fallback must be a state instead of something like

    <div>Loading...</div> 

It must be the same component instance as the currently rendered page. Here is the source code for it.

Han Moe Htet
  • 692
  • 1
  • 6
  • 10
0

Is it possible to keep the current view component mounted until the "lazy loaded" component is ready to be rendered?

Yes, my answer to a different question also applies here. Without seeing a code example, I don't know your exact situation - but you can achieve this behavior by simply wrapping your entire router Switch in Suspense.

For example:

    <Suspense fallback={<PageLoading />}>
      <Switch>
        <Route exact path="/" component={Page1} />
        <Route path="/page-2" component={Page2} />
        <Route path="/page-3" component={Page3} />
      </Switch>
    </Suspense>