WHAT
It is a performance pattern, that allows us on import things over network, on some interaction.
This can be done using React.lazy
, Suspense
, Dynamic Import
& facade pattern
.
Instead of Eager Loading
(Loading resources immediately, on page load) , we load them on interaction.
Facade pattern
: It is a design pattern that provides a simplified interface to a more complex subsystem. Means we mimic the UI of the deferred resource using HTML/CSS/JS.
Examples :
-
Showing 3rd Party Widgets
: We load the 3rd party widget on interaction, initially we show a facade.- Video Embeds
- Chat Widgets
- Social Media Widgets
-
Non-critical resources
-
EmojiPicker
: that didn't used that much. -
Filters
-
Authentication SDK's
: Our app supports 3rd party authentication, so loading that on initial render will be a bad idea. Instead import it on interaction - Basically on button click or show a facade to the user.-
Infrequently used features
-
Scroll to Top
: Load animation libraries (e.g., react-scroll) only when the user clicks a "Back to Top" button.
const ScrollToTop = ()=>{ const [scrollLibrary,setScrollLibrary] = useState(null); const handleScroll = ()=>{ import('react-scroll').then((module)=>{ setScrollLibrary(module); module.animateScroll.scrollToTop(); }) } return <> <button onClick={handleScroll}>Back to Top</button> </> }
-
Export
: Defer loading libraries for generating PDFs or CSVs (e.g., jsPDF) until the user clicks an export button. -
Analytics
: Don't preload/prefecth this as that many users don't use it. -
Tabbed Interface
: Load the tab component only when the user clicks a tab.
const Tab1 = React.lazy(()=>import('./Tab1)); const Tab2 = React.lazy(()=>import('./Tab2)); const tabbedInterface = ()=>{ const [activeTab,setActiveTab] = useState(0); return <> <button onClick={()=>setActiveTab(0)}>Tab 1</button> <button onClick={()=>setActiveTab(1)}>Tab 2</button> <Suspense fallback={<div>Loading...</div>}> // In Server side use Loadable Component {activeTab === 0 && <Tab1 />} {activeTab === 1 && <Tab2 />} </Suspense> </>; }
-
HOW
Facade for a Youtube Video Embed
const YoutubeFacade = ()=>{
const [isEmbedLoaded,setIsEmbedLoaded] = useState(false);
return <div classname='youtube-embed'>
{
!isEmbedLoaded ?
<>
<img
src={`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`}
alt="Video thumbnail"
style={{ width: '100%', height: '100%' }}
/>
<button
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
padding: '10px 20px',
background: 'red',
color: 'white',
border: 'none',
}}
>
Play
</button>
</div>
</>:<>
<iframe
width="560"
height="315"
src={`https://www.youtube.com/embed/${videoId}?autoplay=1`}
frameBorder="0"
allow="autoplay; encrypted-media"
allowFullScreen
/>
</>
}
</div>
}
Only after the user clicks the facade does the element get added to the DOM with the.
Don't use this when
- you need to do autoplay
- you need that for SEO
Facade for a Login with Google
const GoogleLoginFacade = ()=>{
const [isSdkLoaded,setIsSdkLoaded] = useState(false);
const loadGoogleSDK = ()=>{
import("./google-auth").then((module)=>{ // a new bundle is fetched with some autogenerated name, if you want a more friendly then you define its name by doing this - import(/*webpackChunkName:"google-sdk"*/ './google-auth')
module.initGoogleSignIn();
setIsSdkLoaded(true);
})
}
return <>
{
!isSdkLoaded ?
<>
<div className='google-login-button'>
<button onClick={loadGoogleSDK}>Login with Google</button>
</div>
</>:<>
<div id="google-signin-button" />
</>
}
</>
}
// This get loaded on demand
// google-auth.js
export const initGoogleSignIn = () => {
const script = document.createElement('script');
script.src = 'https://apis.google.com/js/client:platform.js?onload=showLoginScreen';
script.async = true;
document.body.appendChild(script);
};
Top comments (0)