Reputation: 947
On this repo: https://github.com/tlg-265/react-app-vanilla
$ git clone https://github.com/tlg-265/react-app-vanilla
$ cd react-app-vanilla
$ yarn
$ yarn start
I have a dummy app with just 3 pages: { Page1, Page2, Page3 }
.
My goal is: Split and lazy load Page3
and prevent flickering when transitioning to it.
With my existing code the splitting and lazy loading works well but there is a flickering when transitioning from Page2
to Page3
.
Here are some of the main files:
react-app-vanilla/src/App.js
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { ReactLazyPreload } from './utils/Functions';
import './App.css';
import Page1 from './components/Page1';
import Page2 from './components/Page2';
const Page3 = ReactLazyPreload(() => import(/* webpackChunkName: "page-3" */ './components/Page3'));
function App() {
return (
<Router history={window.history}>
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/page-2" component={Page2} />
<Suspense fallback={"Loading"}>
<Route path="/page-3" component={Page3} />
</Suspense>
</Switch>
</Router>
);
}
export default App;
react-app-vanilla/src/components/Page2.js
import React from 'react';
import { ReactLazyPreload } from '../utils/Functions';
const Page3 = ReactLazyPreload(() => import(/* webpackChunkName: "page-3" */ './Page3'));
class Page2 extends React.Component {
componentDidMount() {
Page3.preload();
}
handleNext = (e) => {
e.preventDefault();
this.props.history.push('/page-3');
};
render() {
return (
<>
<h1>Page 2</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer gravida leo in pharetra sagittis. Donec sed tempus ex, nec rhoncus justo. Phasellus auctor diam eleifend, vestibulum justo ac, ultrices ipsum. Donec pretium augue ante, eget eleifend mi vehicula eu. Donec vitae sem erat. Vestibulum tincidunt suscipit ex, vitae condimentum odio ornare in. Vestibulum erat neque, semper sit amet suscipit vel, malesuada in diam. Morbi ut eros eget lectus sodales rhoncus.</p>
<div style={{ textAlign: 'center' }}>
<button type="button" onClick={this.handleNext} className="button-next">NEXT</button>
</div>
</>
)
}
}
export default Page2;
react-app-vanilla/src/utils/Functions.js
import React from "react";
export const ReactLazyPreload = importStatement => {
const Component = React.lazy(importStatement);
Component.preload = importStatement;
return Component;
};
My last commit is based on some suggestions I found on this link:
https://blog.maximeheckel.com/posts/preloading-views-with-react/
but unfortunatelly it is still flickering.
Here you have a preview of my changes:
Any idea on how to prevent the flickering from Page2
to Page 3
?
You are welcomed to try it by yourself by following the instructions at the top.
Thanks!
Upvotes: 5
Views: 6995
Reputation: 843
Try using startTransition in Page2 component
import { startTransition } from 'react';
..........
..........
componentDidMount() {
startTransition(() => {
Page3.preload();
});
}
..........
..........
You can find more details about this using below react documentation.
https://react.dev/reference/react/Suspense#preventing-already-revealed-content-from-hiding
Upvotes: 0
Reputation: 69
I had the same issue, and after first trying a similar approach as @Han Moe Htet, I realized that the answer is much more simple. Instead of wrapping the single Route with Suspense, wrap the entire router Switch in Suspense. This prevents the fallback from flickering momentarily the first time /page-3
loads.
For example:
<Suspense fallback={"Loading"}>
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/page-2" component={Page2} />
<Route path="/page-3" component={Page3} />
</Switch>
</Suspense>
@Viewsonic I just tried this in your repo and it resolved the issue for me.
Upvotes: 2
Reputation: 743
It depends on what do you want to disply while loading the next page. Many answers on the internet show using a loading spinner with a delay to prevent flickering.
The current approach I am using update the fallback whenever a page is rendered. I don't use a <Loading />
component but rather display the currently mounted page while preparing for the next page.
Here is the demo and source code.
Upvotes: 10