DEV Community

Cover image for Declarative Resource Loading with React
José Muñoz
José Muñoz

Posted on

Declarative Resource Loading with React

^(photo by @alexjones on unsplash.com)

Loading Stuff is Hard

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

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.

lazy example

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.

Data Fetching

Look at the following example of a traditional class-based data fetching component:

class exampled

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?

hooks example

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 example

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.

Other Resources

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.

suspense image

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.

suspense with thumbnail

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.

Strict Mode

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.

Concurrent Rendering

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.

create root

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:

GitHub logo jdmg94 / sdjs-suspense-demo

A Simple Data Fetch Demo using Classes, Hooks, and Suspense. Circa 2019

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.

Usage

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…

Top comments (0)