It's been a time since the React's team has introduced Suspense and concurrent rendering. With hooks, it's the new amazing feature that React is providing.
We are "able" (at least in dev-mode) to:
- create applications that manage specific priorities over our asynchronous operations
- manage asynchronous computations just like if they were synchronous
- use functional components everywhere instead of classes
I'm really excited about the future of React!
And today, I wanted to talk about a Suspense
specific feature which is the lazy
function that was introduced in React v16.6.
This function aims to provide a simple way to rely on bundler's code splitting using some code like:
import React, { lazy, Suspense }Â from 'react';
const LazyComponent = lazy(() => import('./components/myComponent'));
const App = () => (
<Suspense fallback={<div>Waiting...</div>}>
<LazyComponent />
</Suspense>
)
What the?..
It can be a bit disturbing at first, how can we:
- code split our code, which is a build time feature
- make an asychronous computation that creates a component
- use an (async?) component in a render function which aims to be synchronous
using 2-3 lines?...!
Suspense...🤯
This is not that magic and can exist thanks to the Suspense
component.
This component is a bit special and whenever you will throw
a Promise
in one of its children, it will catch
that promise, resolve it and re-render its children.
Did you know that you were able to throw something else than errors in JavaScript?!
This is why it's called Suspense
: it suspends the normal execution flow of your application thanks to the throw
keyword, and make some specific computations before "resuming" it. It doesn't resume it at the exact position of your code, but at least, it re-renders its children which make you feel like you were getting back to the old execution position.
I tried to write about it in this Medium post but without success - my thoughts at that period were not that organised.
I won't keep the "suspense" for now, so let's check one implementation of the lazy
function I've came across:
import React from "react";
let IDS = 0;
const loaded = {};
export const lazy = modulePathResolver => {
const id = IDS++;
return props => {
const LoadedComponent = loaded[id];
if (LoadedComponent) {
return <LoadedComponent {...props} />;
}
throw modulePathResolver().then(lazyModule => {
const Component = lazyModule.default;
loaded[id] = Component;
});
};
};
It's available on github gist if you want to play with it.
Obviously, this snippet only works if the component is used inside a Suspense
parent.
The lazy
function accepts one argument modulePathResolver
which is a Promise
that resolved the module containing your lazy component.
The lazy
function returns a function which is in fact a (functional) component. All the references to id
exist only to make sure that the component has only loaded once.
If you take a closer look at the code, it really looks like a cache system, but instead of setting the cached value directly, it throw
s a promise that wraps the cache setting so that the Suspense parent can resolve it, lazily.
And you know what? Since it's an asynchronous operation, it can take some time to execute, milliseconds, seconds or even minutes. And what is displayed during the asynchronous resolution? The fallback
prop of the Suspense
component is displayed! Nothing more!
And now, what?
You have an idea of the way the lazy
function is working but you also now know how Suspense
is working. You can now imagine every kind of asynchronous resolution without creating isLoading
states everywhere, every-time. What about lazy image loading with low-high quality pictures 😉?
Jared Palmer is really good advocate of this and has talked about it in multiple talks he has given like the one at Chain React 2019
My point on this feature is that it also pushes the side effect computations we use to make to the edge of our applications. We can make asynchronous stuff using synchronous APIs without headaches. It makes me think of monads and the capability to isolate and compose what causes (side) effects with trivial code.
Isn't this pretty cool?!
Happy coding everyone! React has some beautiful days to come! 🚀
Top comments (0)