DEV Community

loading...

Trying Lazy Loading and getting flicker?

Ben Mitchinson
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.
・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!

Discussion (9)

Collapse
devdrake0 profile image
Si

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?

Collapse
bmitchinson profile image
Ben Mitchinson Author

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.

Collapse
devdrake0 profile image
Si

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.

Collapse
glebirovich profile image
Gleb Irovich • Edited

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)
);
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode
Collapse
bmitchinson profile image
Ben Mitchinson Author • Edited

@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.

Collapse
glebirovich profile image
Gleb Irovich

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

Collapse
glebirovich profile image
Gleb Irovich

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.

Collapse
glebirovich profile image
Gleb Irovich

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

Collapse
vinaysharma14 profile image
Vinay Sharma

Hi Gleb, creating a reusable function results in the following error:

Error: Cannot find module './Home'