Single Page Applications (SPAs) typically work by downloading a bundle that contains all the scripts your app needs. Routing and URL changes in the browser's address bar are handled via HTML5 history API. This creates an incredibly powerful user experience since the browser never needs to refresh to load any part of your application - you can navigate from one page/URL to another instantly.
While this sounds good in theory, in practice, things are ugly. One of the biggest problems is the bundle size - in an SPA of medium complexity, the bundle size can easily reach megabytes. Now, such big files not only take a long time to download but also are not cached by the browser - so they need to be fetched again and again which in turn makes your app look sluggish.
There have been many attempts to remedy this - asynchronous and deferred loading of scripts, and code splitting. Code Splitting refers to the technique of splitting your mega bundle into smaller chunks - the idea being that you download a core or critical part of your app as soon as possible and then load rest of the code on demand. This solves the problems mentioned above but achieving code splitting turns out to be incredibly hard.
One of the reason being, that it's hard for tools like Webpack to figure out how to split the code effectively, with less manual intervention. In AMD world, define/require helped you define code split points - so you can load critical parts first and load rest on demand in an asynchronous fashion.
In React world, lazylets you do so effectively with less manual intervention. By defining something lazy, you indicate that this part of the code is non-critical and can be loaded behind the scenes, on demand.
The () => import('./OtherComponent') the syntax is also known as dynamic import which is different from a static import: import OtherComponent from './OtherComponent'. Dynamic imports are asynchronous and thus they always return a Promise. A Promise ensures that the code that depends on this lazily loaded module, executes only after the script has loaded.
Consider the following snippet:
const PurchaseHistory = lazy(() => import('./components/PurchaseHistory'))
class App extends Component {
state = {
purchases: [ ],
};
componentDidMount() {
fetch(`some/api/that/returns/data`)
.then(res => res.json())
.then(res => {
this.setState({ purchases: res.data });
});
}
render() {
return (
<div className="app">
<Suspense fallback={Loader}>
<PurchaseHistory {...purchases} />
</Suspense>
</div>
);
}
}
In the above code, React can immediately render the app with the Loader and until the fetch succeeds, it doesn't even need to load the lazy PurchaseHistory component. This saves download times, reduces memory footprint and saves CPU cycles that would be needed to download and process the PurchaseHistory component.
Suspense is new API in React 16.6.
Together with another upcoming feature called Concurrent Rendering, the above code ensures that React renders the app pretty fast (faster than React 16). It can do so because React knows which parts are crucial and which parts are not.
For further reading, I suggest this blog post that talks about all 3 features with code examples.