Show a loader while navigating on a single-page application (SPA) with Sitecore JSS and React
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import i18n from 'i18next'; | |
import Helmet from 'react-helmet'; | |
import TopLoader from 'react-top-loader'; | |
import { isEditorActive, withSitecoreContext } from '@sitecore-jss/sitecore-jss-react'; | |
import { layoutServiceFactory } from './lib/layout-service-factory'; | |
import config from './temp/config'; | |
import Layout from './Layout'; | |
import NotFound from './NotFound'; | |
/* eslint-disable no-console */ | |
// Dynamic route handler for Sitecore items. | |
// Because JSS app routes are defined in Sitecore, traditional static React routing isn't enough - | |
// we need to be able to load dynamic route data from Sitecore after the client side route changes. | |
// So react-router delegates all route rendering to this handler, which attempts to get the right | |
// route data from Sitecore - and if none exists, renders the not found component. | |
class RouteHandler extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { loading: false }; | |
// tell i18next to sync its current language with the route language | |
this.updateLanguage(); | |
} | |
componentDidMount() { | |
// If we are not using SSR we have to load layout data | |
if (!this.props.isSSR) { | |
this.updateLayoutData(); | |
} | |
} | |
/** | |
* Loads route data from Sitecore Layout Service into state.routeData | |
*/ | |
updateLayoutData() { | |
let sitecoreRoutePath = this.props.route.match.params.sitecoreRoute || '/'; | |
if (!sitecoreRoutePath.startsWith('/')) { | |
sitecoreRoutePath = `/${sitecoreRoutePath}`; | |
} | |
const language = this.getLanguage(); | |
// instantiate the dictionary service. | |
const layoutServiceInstance = layoutServiceFactory.create(); | |
this.setState({ loading: true }); | |
// get the route data for the new route | |
layoutServiceInstance.fetchLayoutData(sitecoreRoutePath, language).then((routeData) => { | |
this.props.updateSitecoreContext(routeData); | |
this.setState({ loading: false }); | |
}); | |
} | |
getLanguage() { | |
return ( | |
this.props.route.match.params.lang || | |
this.props.sitecoreContext.language || | |
config.defaultLanguage | |
); | |
} | |
/** | |
* Updates the current app language to match the route data. | |
*/ | |
updateLanguage() { | |
const newLanguage = this.getLanguage(); | |
if (i18n.language !== newLanguage) { | |
i18n.changeLanguage(newLanguage); | |
} | |
} | |
componentDidUpdate(previousProps) { | |
const existingRoute = previousProps.route.match.url; | |
const newRoute = this.props.route.match.url; | |
// don't change state (refetch route data) if the route has not changed | |
if (existingRoute === newRoute) { | |
return; | |
} | |
// if in Sitecore editor - force reload instead of route data update | |
// avoids confusing Sitecore's editing JS | |
if (isEditorActive()) { | |
window.location.assign(newRoute); | |
return; | |
} | |
this.updateLanguage(); | |
this.updateLayoutData(); | |
} | |
render() { | |
const layoutData = this.props.sitecoreContext; | |
// Note: this is client-side only 404 handling. Server-side 404 handling is the responsibility | |
// of the server being used (i.e. node-headless-ssr-proxy and Sitecore intergrated rendering know how to send 404 status codes). | |
// `route` is null in case if route is not found | |
if (layoutData.route === null) { | |
return ( | |
<div> | |
<Helmet> | |
<title>{i18n.t('Page not found')}</title> | |
</Helmet> | |
<NotFound context={layoutData} /> | |
</div> | |
); | |
} | |
// Don't render anything if the route data or dictionary data is not fully loaded yet. | |
// This is a good place for a "Loading" component, if one is needed. | |
if (!layoutData.route) { | |
return null; | |
} | |
// Render the app's root structural layout | |
return ( | |
<React.Fragment> | |
{/* {this.state.loading && <div>Loading...</div>} */} | |
<TopLoader | |
backgroundColor="#eee6ff" | |
show={this.state.loading} | |
fixed={true} | |
color="#0000e4" | |
duration={600} | |
thickness={5} | |
/> | |
<Layout route={layoutData.route} /> | |
</React.Fragment> | |
); | |
} | |
} | |
export default withSitecoreContext({ updatable: true })(RouteHandler); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment