photo by @alexjones on unsplash.com
how many of you write the word isLoading 10x a day? Often times we want to make our application responsive even in slow connections, so we have loading indicators to give the user feedback that the app is working. If you think about your application, a lot of time and energy is focused on coordinating when stuff comes in, when it comes out, and what to show the user. This is any resource our browser uses, even resources that we don't fetch explicitly like images, fonts, code, etc. impact our time to interactive and UX. In this post, we'll take a look at a few approaches to load resources in our apps and how this is going to play in the near future.
Code splitting is when you only send the code that’s required to load the page you are visiting, and then the rest of the code is sent to the client as the user navigates. Although there are a lot of other ways of achieving this, React has a couple of APIs which help us split the bundle into chunks of code that are grouped in the most efficient way to reduce their file size. The first API we need in order to split our application is called lazy, it is super simple, it takes a function that returns a dynamic import and then that’s a drop-in replacement for your component.
the other API is called Suspense. Suspense helps us determine if the resources we are trying to call are available to use and displays an alternative loading state while the resource becomes available. In other words, Suspense is a declarative loading state API.
Look at the following example of a traditional class-based data fetching component:
it initializes the state on the constructor, fetches the data on-mount, and when it renders it evaluates if there is data to present or if it's ok to display a loader fallback instead. You must be thinking, of course using hooks would make it better right?
With Hooks, you fetch data using effects and imperatively checking the loading state to display a loader until the resource becomes available. We reduced some boilerplate code and made it cleaner, but it is still the same process, it still imperatively decides whether to present a loader or content.
Until now, we have only been able to use Suspense to load dynamic code with lazy, but in reality, we can use it for other types of resources too. You can suspend any element in your component hierarchy, if the calling resource can’t suspend the rendering of that subtree and provide a fallback it will look up the tree until it finds one that can or it errors out, it works exactly like an error boundary.
Suspense allows async resources to behave as if they had sync access. We don't even have a loader on our fetching component now, we just let React know that the resource ain't ready yet and Suspense will provide a fallback somewhere along the tree to wait for our resources to be ready to render.
Not only does Suspense improve our DX by reducing our lines of code exponentially but it also allows for a smoother transition between loading states like when your app goes from lazy loading the code to fetching data.
Another great example is images, sometimes our pages render and some images are missing, the requests for those resources can be on the heavier side. Let's do our new trick and load an image using Suspense.
Of course, you don't have to do this yourself, the-platform is a nifty library that has your back with wrapped components for images and other resources that are Suspense-ready.
As a fallback, you could have a regular
<img/> tag with a lower-res thumbnail instead of a spinner and it will provide a smooth transition between the thumbnail and the full-size image.
So now we have a few resources loading and our application might start to feel slow if we keep that loader going, we need to decide how much are we willing to wait for each resource. I definitely need to retrieve the code and data first, but I could get away with showing a low-res thumbnail for a few seconds longer in order to deliver an almost complete UI. Suspense provides us with a second prop called
maxDuration and this prop allows us to specify in milis how much should it wait till it provides a fallback or content.
maxDuration will only work when certain conditions are met, this is where things get a little tricky.
In order for React to appropriately evaluate what is going on, there should be no deprecated APIs in the suspended tree. to help us identify possible issues React exposes a tag called
StrictMode. Strict mode is a stable API and you can use it right now in production, nothing will break, however, if there are any deprecated APIs used within its children you will be getting errors on your browser's console.
If you wrapped your app in strict-mode and your console is still clear, congratulations! You are now able to activate concurrent mode and start taking advantage of
maxDuration. But what exactly is Concurrent mode? In the words of Dan Abramov:
Concurrent Mode lets React apps be more responsive by rendering component trees without blocking the main thread. It is opt-in and allows React to interrupt a long-running render (for example, rendering a new feed story) to handle a high-priority event (for example, text input or hover). Concurrent Mode also improves the user experience of Suspense by skipping unnecessary loading states on fast connections.
There are 2 ways of enabling concurrent mode in your application, again, these features are experimental and expected to ship sometime around Q2 2019. React-dom exposes a function called
createRoot (currently unstable_createRoot), it receives the dom reference to the element where the app will be rendered, you can chain a
render method with the app instance.
As an opt-in feature, React exposes a tag called
ConcurrentMode which you can use to wrap subsections of your app to enable concurrent rendering only in their subtrees.
For a demo visit https://sdjs-demo.jdmg94.now.sh
For the full source visit:
San Diego JS React Monthly Demo
A quick data fetching demo, illustrates data fetching with classes, hooks, and suspense. Includes code-splitting with React lazy, Concurrent mode, and both Data and Image fetching using suspense.
the options are displayed on the main page, both classes and hooks work in virtually the same way, they are both loading data on mount and imperatively so if things don't come through in a timely manner, you can see a small 'jump' on the loader. By contrast, using Suspense, not only do we reduce the code boilerplate even further, but allows us to treat the data as if it was a synchronous operation, it will use the algebraic effect of an error boundary to find the nearest fallback up its tree and wait until everything is ready to render taking advantage of concurrent mode.
If you open the suspense example page, you will…
Most people want to make things perfect. Sometimes we evaluate the complexity of an upcoming goal or a problem. So, the fear to not complete it perfectly or "wrong" (Yeah, who are judges? 🤔) stops us even from trying.