An attempt to explain React Suspense to myself.
My “basically” projects aim to take fascinating concepts and break them down into more “basic”/bite-size versions. This project highlights React’s suspense for data fetching functionality, creating a minimal example that doesn’t even use React, but mimics its behavior in a basic sense.
See demo | See documented source code
⚠️ Disclaimer: I do not work on React core at all and this is a huge guess. I have not extensively looked at the source code of this feature and I don’t really know what I’m doing, so please take this with a grain of salt and refer to the React core team for offical things. This repo here is my personal exploration to learn these things publicly (thanks, @sw-yx). I might have gotten it either partially or completely wrong. In this case, hopefully someone will gently correct me if things are not quite right and help me figure out the “truth”/the right way this works.
Take a look at index.js to see how (I think) it works. We have:
Each of these should be well documented and easy to grok. If they aren’t, please open an issue. Also, if I got something wrong, please please please let me know by opening an issue.
I kind of accidentally wrote this while researching/learning about algebraic effects. I’ve heard a lot of this terminology around React’s shiny new (and still experimental!) Concurrent Mode and so I wondered how it fit in. Turns out this might be how. ¯\_(ツ)_/¯
. I think this is really novel because algebraic effects allow code to behave in a synchronous way while still preserving asynchronous functionality, pausing, performing an effect, and resuming where necessary.
async
/await
allows similar behavior, but any function automatically returns a Promise
when prepended with the async
keyword, which kinda sorta “contaminates” it, and potentially leads to a special kind of async hell when we want to do something synchronous.
With algebraic effects, functions can be synchronous while allowing an external async effect handler to deal with the async part. The cool thing about this is the handler could be swapped out for pretty much anything centrally, and “user code” stays exactly the same.
Another benefit is that it literally “defers” to an effect handler higher in scope. I think the best analogy for algebraic effects is try/catch
blocks. No matter where the code comes from that is enclosed in try
, a catch
handler in a different scope/context/file(!) is fired and can handle exceptions from literally anywhere. This is quite cool. Applied to data fetching, one can try
to use data that might be available. If it’s not available, the exception (or effect handler) of “I have no data” is caught wherever we have a handler and it is processed accordingly.
If you’d like to have a chat or discuss things more, or even request more explainer-type things, @ me on twitter.