loading...

Trying Lazy Loading and getting flicker?

bmitchinson profile image Ben Mitchinson ・1 min read

Hey ya'll! Using suspense + lazy for the first time, would really really appreciate some help.

I've recently enabled suspense and lazy loading in my application, and when I apply them to my <Router>, I get a solid load + performance boost.

Was wondering though, in-between clicking on pages, I get this huge white flicker. I'm using it like so:

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
{...}

const NotFound = lazy(() => import('./components/NotFound'));
const Feed = lazy(() => import('./components/Feed'));
const Food = lazy(() => import('./components/Food'));
{...}
import('./components/DEBUGEasyPeasyContents'));

const App: React.SFC = () => {
    const isLoggedIn = useStoreState(state => state.userModel.isLoggedIn);

    const Spinner = <Spin size="large" style={{ width: '100%', margin: 'auto', padding: '10%' }} />;

    return (
        <>
            <ReactNotification />
            <Router>
                <Suspense fallback={wrapHOC(Spinner)}>
                    <Switch>
                        {isLoggedIn && (
                            <>
                                <Route path="/login" exact={true} component={Login} />
                                <Route path="/changePassword" exact={true} component={wrapHOC(ChangePassword)} />
                                <Route path="/account" exact={true} component={wrapHOC(AccountInfo)} />
                                <Route path="/dashboard" exact={true} component={wrapHOC(Dashboard)} />
                            {...}
                    </Switch>
                </Suspense>
            </Router>
        </>
    );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

In-between clicking on pages, the whole page flashes white for like 1.3ms according to the chrome screenshot inspector. It ruins the performance boost for me, so I'm desperate to figure out the cause.

Any advice appreciated, thanks!

Posted on by:

bmitchinson profile

Ben Mitchinson

@bmitchinson

Web Software Engineering focused on code quality, learning new things, and creating effective processes for agile development. Likes to make apps that are fun to use.

Discussion

pic
Editor guide
 

Throttle your speed to like Slow 3G or something, and see if you can capture what the white background is.

Is it displaying a loading screen, which flickers because it loads so quickly?

 

It does! How do I avoid this? I was hoping my spinner would be rendered without a flash? It doesn't even render my spinner during this time it's only white.

 

It's "flashing" because you're page is loading too quickly, so the spinner loads and then disappears because the job is over.

I'm not a frontend dev, or a designer, but I would find a loading mechanism that doesn't cause a flash.

For example, a skeleton screen.

 

Hey! You see flickering because loading happens too fast and spinner changes immediately to the route content. You can try setting a minimal delay, since lazy can return regular Promise.

const Food = lazy(() => Promise.all([
  import('./components/Food'),
  new Promise((resolve) => setTimeout(resolve, 3000)) // ensures minimal delay
 ]).then(([module]) => module)
);

or create a function out of it, which would reduce the boilerplate:

const lazyDelayed = (path: string, delay = 3000) => {
  return lazy(() => Promise.all([
    import(path),
    new Promise((resolve) => setTimeout(resolve, delay)) // ensures minimal delay
  ]).then(([module]) => module));
}

const Food = lazyDelayed('./components/Food')
 

@gleb Thanks for this suggestion, it's behaving like you described "loading happens too fast and spinner changes immediately to the route content"

I tried your solution, and it added a delay to the load, where the white screen was extended by 3 seconds, then went to the component. Did you get this solution from here? stackoverflow.com/questions/541589...

I'm still don't understand the difference between the first and second solutions listed there in that SO post. Tried both to no avail.

I'll try to create something reproducible in typescript create-react-app.

 

Here is a short working demo:
github.com/GlebIrovich/suspense-demo

I think something is wrong with the HOC. Because if you look at my implementation, valid Route can only accept HOC like a that: <Route path="/changePassword" exact={true} component={() => withHOC(LazyContent)} />

Hope it helps

 

Regarding the solution on SO.
First one adds time on top of the time required for import. So if import takes 2 sec and delay is 3 sec, totally it will take 5 sec.
The second approach uses Promise.all, which makes the same work "in parallel". So if import takes 2 sec and delay is 3 sec, totally it will take 3 sec. So the operation will take AT LEAST the time specified in the delay.

 

I would suspect then that your HOC not returning properly. Could you validate if spinner is shown without using hoc?