React for Two Computers
overreacted.io
overreacted.io
<alert>
<concat>
Hello,
<prompt>Who are you?</prompt>
</concat>
</alert>
> This blueprint of “potential calls” looks code, but it acts like data. It’s structurally similar to function calls but it is more passive, inert, open to interpretation. We’re yet to send this blueprint to the other computer which will actually interpret it.You've reinvented Free Monads: https://www.haskellforall.com/2012/06/you-could-have-invente...
That article is a bit dense if you're less familiar with Haskell, but the basic idea is that you can write code that looks like:
do
name <- prompt "Who are you?"
message <- concat "Hello, " name
alert message
And what actually gets assembled is something resembling a syntax tree of operations, waiting to be interpreted.You could interpret it locally, or have the interpreter send requests to another computer. And (if such are your desired semantics) the interpreter can short-circuit the whole thing and return an error message if any step fails - similar to how you describe these as being "potential function calls", i.e. calls which as of yet may or may not successfully execute.
Analogously, JavaScript already has a monad-like construct which can achieve something similar:
async () => {
const name = await prompt()
const message = await concat("Hello, ", World)
const result = await alert(message)
return result
}
But this is pure code, rather than data that can be interpreted in a custom way - it will always be "interpreted" by the runtime itself. The Haskell example is somewhat of a blend between data and code (it desugars to a tree of closures calling each other in sequence, which you have a lot of control over the execution of).Your React-style example attempts to make this concept into pure data. Though in some ways, a DSL becoming pure data wraps around and becomes pure code again. You need to parse it, semantically analyze it, and then execute it - in other words, you've invented a compiler.
Wat also happens to have a good concurrency model, and a portable bytecode. Worth looking at in this space.
If you hydrate on the client, you need to run the same exact code on the server and again on the client - however, that means you now might need to bring in a big syntax highlighting library to the client.
With RSC all of that is done server side, so you don't need to ship the syntax highlighting library.
The main use cases where server components provide a large benefit are:
1) Web sites with a very large number of components, but where any one page or session will use a very small (and unpredictable) subset of those components. These are things like social media feeds, where there could be many thousands of types of feed items, but most people will see just a handful of them. This is a tricky problem for code-splitting.
2) Components which require a lot of code to perform a render relative to the size of the rendered output. This is stuff like Markdown renderers and syntax highlighting. This benefit becomes even more obvious if the rendered output is mostly static but does contain some islands of interactivity.
In particular, the callback to Miguel's article about async/await (https://tirania.org/blog/archive/2013/Aug-15.html) is intentional. I'd like to see some parts of my article as making a similar argument for 'use client' / 'use server' and the concept of first-class cross-environment references module system.
And as for practical benefits, I think the main benefit is composition--ability to compose and weave Server/Client building blocks together. Of course there's been myriads of approaches to server/client apps but I've never seen anything with the same compositional properties as RSC. I think Sam's talk (https://www.youtube.com/watch?app=desktop&v=9CN9RCzznZc) makes an amazing argument in favor of that viewpoint so I'll defer to him.
Obviously the technology is impressive, basically serializing code over the network.
I’ll look into Sam’s talk.
What is value proposition of react component in comparison to older “mvc-inspired” frameworks? That you can drop a component in your code and call it a day. Don’t need to wire up separate controllers, models and views. Just one thing.
Now, if you want to add “auth” to your app (in react meta framework like next) you need to add component and probably add some routes and inject some middleware. With server components, you just add one component somewhere in the root of your app. Just one thing.
All that over the wire format, suspended data fetching etc, are a result of solving that problem and keeping in mind that those components are slightly different. It takes more work, but in return we can interleave both types of components nearly freely.
So again, you want to add one thing “auth”, but need to add code in multiple places in your app. Server components promise to encapsulate that. Idea is that you can grab a component from npm, and it will handle all of that orchestration for you, and component will be your only one interface, so all configuration can be passed as props (of course it will be taken from your env/secret).
The promise is that you can encapsulate inside a components both client and server code.
They aren't reinventing or proposing anything new.
Most of the complaints in the comments here are probably due to author not saying "hey we're going to pretend to invent RSC so you get how it works, hop in"
https://codesandbox.io/p/sandbox/8dgdz8
What's overengineered here?
My point is that the entire approach is architecturally far more complex than is suitable for the vast majority of projects. You have influence and people will make real world decisions to choose unnecessary complexity because of this article.
Why do 150 lines when 10 lines of PHP is functionally equivalent to the user? The user doesn't care about your execution model. They just want some information or to submit a form. Adopting an RSC-like technology makes development more complicated and take longer.
If you're building Figma or something then sure, go nuts with the complicated things. The tradeoff probably makes sense.
For the other 99% of us, this model just gets in the way.
There are many ways to mix and match client(js) and server (php) in a composable way without react. Components can be created on the server side as well (mixing html+js+php). Only thing lacking is DX and type checking.
If you don’t see what’s new about this I encourage you to look deeper because this definitely isn’t what backend frameworks have been doing for years so you are missing something.
> I’ve given up on the idea of converting this talk into a post form, nor do I think it’s possible. But I wanted to jot down a few notes that are complementary to the talk. I’m going to assume that you have watched the talk itself. This is just the stuff that wasn’t coherent enough to make the cut—the loose threads I couldn’t tie together.
Thanks, I must have skipped that part. It's hard for me to sit down watch 30 minutes of a video, so I like reading text more, but I will try to watch it tomorrow.
Would love recommendations for other things like this where the style is one of discovery!! (like math textbooks that make you feel like you invented something?)
Author unfortunately fails to justify or provide a demonstration that justifies the increased complexity over current methodology.
Interesting exploration of an unexplored space, but should be more concise (and use either better or no attempts at humour).
> In the Early world, you dissolve all the Early Components with interpret. This gives you a string that represents how to finish the computation in the Late world: [code]
> In the Late world, you parse that string, load the references, and then dissolve the Late Components with interpret. That leaves you with a tree of Primitives: [code]
> Finally, those Primitives are ready to be turned into DOM or some other format: [code]
Not enough engineers engage with the concept of code and programming and as an industry we suffer for it. The beginning of this post does a phenomenal job of simplifying very basic but high level concepts as “what is a function” “what is a tag”.
99% or programmers don’t think about these things like this, and so get confused when these building blocks are manipulated and presented in seemingly strange ways like React components or server components or whatever. By breaking down these concepts (functions, blueprints) and having rebuilding them with simple definitions it allows the reader to start their mental model fresh and go from there.
This is a masterclass in technical communication.
Who gives a shit if it’s long. In fact I’m glad it’s long because every sentence is gold. These are deep subjects and foundational to programming and so ya, talking about them like this can take a few words.
99% of people who take the time to really read and process this post will come away as noticeably improved developers. That kind of bang for the buck is rare!
After reading the post, and re-reading the opening, it almost seems they are about two different posts. e.g. "Jot down a few notes" vs actually going meta with yourself in the middle about how the post isn't even halfway. That doesn't really make a lot of sense.
That you then expect the reader to want to read all this _after_ already having watched the video seems a bit gratuitous? Perhaps it would've been better built as something interactive or graphical so you wouldn't need to explain as much of the mechanics.
I did find it interesting, but had to lol at `{fn, args}`: I've spent the last few years working on Live, a not-React run-time, which has exactly that type for its deferred calls. It was inspired by the same idea of treating JSX tags as just `fn(...args)` in drag.
I've always been curious to hear your opinion on Use.GPU / Live. There is a keynote video linked here https://usegpu.live/ if you're interested.
Re Live, I actually don’t know anything about it! Unfortunately I also don’t know anything about graphics programming so it might be tricky for me to evaluate. I’ll check it out though.