Recreating React Suspense features in Elm
I’ve made a proof-of-concept project trying to replicate some of the react suspense features in this repository. This project is a recreation of this react suspense demo.
What’s good about suspense? Some features caught my attention:
Data load co-located with the view
Usually in Elm the data fetching is at the router or some other place far from the view, having them together can make it easier to follow where the data is going and also move a view around in a more “plug-and-play” manner
Suspend and timeout for loading pattern in fast connections
For slow internet speeds we usually show a “Loading” message for the users, which is great and elm notably helps us a lot about not forgetting that. But, for fast internet connections, a “Loading” flicking really quick all the time can be very annoying.
We do not have to decide between one case or the other though, we can make the experience great for both at once, and suspend helps with that through the timeout component, which shows a “Loading” message, but only after some miliseconds.
Preloading of images
On the web it’s common to render the html as soon as we get it, and different pieces like images load one after another, top to bottom, causing a lot of layout changes until everything’s in place.
But we can have a better experience than that, with suspense, we can wait util everything is fully ready before rendering the screen for the user
Demos
Fast internet connection without Suspense features
Notice how everything flicks constantly, blinks of “Loading…” with very fast layout changes all the time
Slow internet connection without Suspense features
With a slow internet the “Loading…” is ok, but the images load little by little, causing a little bit of agony
Fast internet connection with Suspense features
Everything looks instant now, no more screen flickings or layout change
Slow internet connection with Suspense features
The user now knows it’s loading, but does not receive a broken experience anymore, we wait until everything is ready to show
How it works
Loading data
In order for all this to work, I had to subvert the elm-architecture a little bit to be able to return Cmds while rendering, which means that you can load data, but that’s nicely wrapper around the idea of caches to your date. If you have a view that needs to load some data, you can do it like this:
1 | myView : Model -> String -> CmdHtml Msg |
Adding a timeout
On the example above, the view won’t render until the data is ready, but sometimes you want to render at least something to give feedback to the user, you can do that using the timeout
function, in which you can specify a fallback and a time for it to show. You can also replace this with the suspense
function if you don’t want to have any fallbacks.
The interesting part is that this function can be many parents above with many levels of suspended or regular views inside it, it’s not directly bound to the data loading.
1 | waitForMyView : Model -> String -> CmdHtml Msg |
Preloading images
You can use the function preloadImg
to render a view only after a image is loaded, it’s as simple as this:
1 | myViewWithImg : Model -> CmdHtml Msg |
Other details
There are also other helper functions like mapCmdView
and mapCmdViewList
to help you wrapping the html inside the suspense features, and you also need to add some boilerplate on your main Msg
, Model
and Update
for all this architectural change to work. Check the src/
folder for more examples.
Conclusion
Having suspense features can really help achieving a great user experience. Implementing that in Elm was much harder than just using it in React, but it is definitively possible and the final API became kinda easy to use.
What do you think? Should I turn elm-suspense into a library? Suggestions are welcome!
More details in the repository:
https://github.com/rogeriochaves/elm-suspense
Comments
If you’d like to add a comment, please send a merge request adding your comment here, copying this block as an example