[HN Gopher] React: The Perils of Rehydration
       ___________________________________________________________________
        
       React: The Perils of Rehydration
        
       Author : Epskampie
       Score  : 51 points
       Date   : 2021-12-01 17:07 UTC (1 days ago)
        
 (HTM) web link (www.joshwcomeau.com)
 (TXT) w3m dump (www.joshwcomeau.com)
        
       | p2hari wrote:
       | Just to clarify myself. If I have already logged in and now
       | closed the window. Again open a tab and send request to the
       | server, won't the token/cookies/sessioninfo etc. be sent in the
       | initial request to the page? And if the auth info is sent via the
       | headers, can we not check the user login status (expired
       | sessions) at the server and send the right component back?
        
       | deckard1 wrote:
       | This is more about the perils of React switching from component
       | classes where lifecycle methods were explicit and obvious to the
       | multipurpose leaky abstraction known as useEffect and the hooks
       | API. To anyone that went through the class phase, this issue is
       | probably obvious and well known.
       | 
       | Also, Gatsby. If Gatsby is hiding the React warnings when your
       | SSR diverges from client side, then that's on Gatsby. It's hard
       | enough to debug when you know what's going on. This is like going
       | in blindfolded with both arms tied behind your back.
       | 
       | Luckily this issue only comes up a handful of times.
        
         | bikeshaving wrote:
         | Didn't read the article, but I have to rant a little bit.
         | 
         | Working with `useEffect()` is so fucking miserable. It's a bit
         | like owning a car where the gaps between the panels are just
         | slightly too far apart; there's always the possibility that
         | some data is out of sync because React inexplicably decided to
         | run a thing a tick later than you expected. "Effects" don't
         | actually mix with with the old class component methods, insofar
         | as you'll never be able to get an effect to run before
         | component methods, so you end up having to _gasp_ do side-
         | effects in render against a bunch of "refs" to achieve some
         | semblance of object permanence.
         | 
         | The implication behind the naming of "synchronous effects" as
         | `useLayoutEffect()` is laughable, as though the only time you
         | would ever want to run some code synchronously is for "layout"
         | purposes. My guy, JavaScript's security model is based on
         | execution, you can't copy to the clipboard, request full-
         | screen/picture-in-picture, do countless privileged operations
         | outside the synchronous execution of a native click handler,
         | for instance, and we're going to place all these use-cases
         | under the umbrella term "layout?"
         | 
         | React applications are weird, shuddering messes, where
         | callbacks fire senselessly based on "dependency arrays." Heaven
         | forfend you turn off linting which helps you not violate "the
         | rules of hooks," and even when it's on there are countless
         | gotchas about default parameters and callback functions which
         | are recreated every render. At no point will you ever be able
         | to correctly source the initiator of a "render," because
         | someone in their infinite wisdom decided to unroll the
         | JavaScript call stack as a queue. Stepping out of a component
         | in a debugger always places you in the same `while` loop and
         | you're left wondering where on Earth did this execution start.
         | I don't understand how programmers could be so ignorant of how
         | useful a call stack is, and the React team essentially has to
         | recreate call stacks in error messages and Devtools. Seasoned
         | React developers have all seen "React minified error #185," or
         | "Maximum update depth exceeded." This is essentially a stack
         | overflow error, except of course they did away with the stack.
         | It's not normal for stack overflow errors to be happening as
         | frequently as happens in React applications, and this is just
         | when you're lucky enough to get a warning; a lot of times the
         | page will just hang and no one except your poor users will
         | know.
         | 
         | I find it truly disturbing how professionals can make their and
         | everyone's lives so difficult, how deeply disconnected React is
         | from the actual practice of programming, which is all about
         | understanding how your code executes. My mind is boggled by 5
         | by 5 zoom calls of well-paid "staff engineers" chatting about
         | React and writing tutorial upon tutorial like any of this
         | normal. And these are the loud ones. I pray for the entry-level
         | programmers, the contractors, the voiceless, who aren't beefing
         | on Twitter, who do not have a sense of how strange this kind of
         | development is and don't have the courage to speak up.
        
       | whiddershins wrote:
       | Could this happen in sveltekit?
        
         | benmccann wrote:
         | It seems somewhat unlikely for a number of reasons. The article
         | was quite long, so I didn't read it all, but a key takeaway
         | seemed to be "Gatsby only uses the server-side rendering APIs
         | when building for production". SvelteKit does SSR rendering in
         | development as well, so you wouldn't have the issue of dev and
         | production being different in that way. Also, Svelte's
         | hydration works a bit differently than React's.
        
       | shadowgovt wrote:
       | Honestly, it's a very frustrating aspect of modern web
       | development that developers still have to care about this
       | client/server split for initial render.
       | 
       | JavaScript, via Node, threatens to consume server-side
       | implementations of user-facing output for no other reason than
       | this is an annoying problem that gets even more annoying if your
       | client and server are talking two different scripting languages.
        
         | CGamesPlay wrote:
         | Nobody has yet stepped up and delivered a WASM PHP client side
         | renderer. I bet it would even be size competitive with modern
         | JS frameworks!
        
           | mattnewton wrote:
           | This is a hilarious idea, bonus points if you somehow wrap it
           | in electron with its own local sql lite instance and pitch it
           | as a cross platform desktop app development toolkit.
        
             | ziggus wrote:
             | You laugh, but I'll bet you there's someone pitching this
             | very product to room full of enthralled VCs right now.
        
       | edhelas wrote:
       | If only there was a way to directly send the data when rendering
       | the page server side...
        
         | evnp wrote:
         | I'd suggest you re-read this[1] section of the article --
         | they're talking about "server-side generation" where rendering
         | happens at compile time, not at request time (to avoid delaying
         | responses with on-the-fly render computation). The data you're
         | referring to will not be available at compile time.
         | 
         | [1] https://www.joshwcomeau.com/react/the-perils-of-
         | rehydration/...
        
           | msoad wrote:
           | Yes, but how expensive it is to render on request time
           | anyways? Is it really a better experience to send a "mostly
           | accurate HTML" and "hydrate" it with JavaScript in client?
           | 
           | I am working on this kind of technologies and I totally
           | understand how it works but I'm not convinced this is the
           | right solution. Actual rendering on edge without all this JS
           | soup is what big websites should do.
        
             | handrous wrote:
             | My consistent experience is that sites that simply make a
             | round trip to the server and re-render the world on every
             | non-trivial interaction are _much_ faster than
             | "performant" web apps that try very hard never to request
             | actual HTML from a server, ever.
        
             | technobabbler wrote:
             | It's a cost thing... a lot easier to CDN static HTML files
             | and only make edge calls as necessary.
             | 
             | In our Next.js setup, for example, all pages are served
             | static by default and cached around the world, but every
             | revalidation period (60 seconds or so?), one edge worker
             | will check the data source against the upstream CMS, and
             | rebuild that one page if needed.
             | 
             | That means our max edge worker and API call is 1 per
             | minute, regardless of whether there are 10 visitors or 10
             | million. The others will just get the cached CDN files.
             | 
             | That makes it a lot more affordable.
             | 
             | If you had infinite budget and could pay for edge functions
             | to re-render the page every visit, with or without caching,
             | then more power to you. But not everyone can afford that.
             | 
             | Edit: Also, a secondary benefit is accessibility. Since
             | most of the content is served as a flat HTML, those with
             | outdated browsers/misconfigured ad blockers/javascript
             | turned off can still see the most important part of the
             | content. Maybe they miss some interactivity, but they can
             | still at least read the gist of the article since that's
             | just HTML.
             | 
             | Depending on your specific edge function configuration
             | (i.e. whether it needs web workers or web sockets or such)
             | some browsers may not load that correctly. Dangerous for
             | that to be the only delivery mechanism. But if your edge
             | function just masquerades as a web server and returns plain
             | old HTTP, that shouldn't be a problem.
        
             | bern4444 wrote:
             | Remix [1] solves this problem very well.
             | 
             | Data that is needed by a React component is declared in the
             | same file as the component as a loader function which the
             | server then invokes and renders into the component on the
             | server so the client receives an HTML response with the
             | view ready to go (server side rendering).
             | 
             | I distinguish between server side rendering which is a
             | function invocation that takes place for each request as
             | opposed to pre built HTML which isn't "rendering" anything
             | and instead returning a static, pre-generated file.
             | 
             | With Remix, the client gets an HTML response the server
             | generated with all the data it needs already retrieved (it
             | was retrieved on the server).
             | 
             | The client can then run any additional client side JS but
             | the user doesn't need to wait on the client side JS to see
             | the content they were trying to browse.
             | 
             | Remix can easily be deployed to edge systems like
             | cloudflare workers.
             | 
             | The services that the loader functions hit can be run
             | somewhere else of course as well.
             | 
             | [1] remix.run
        
           | mjlawson wrote:
           | Personally, and I totally recognize that I'm likely arguing a
           | moot point, I don't conflate static-site generation and
           | server-side rendering and I don't think they can or should be
           | used interchangeably as the author indicates. If my site can
           | be hosted by Nginx alone, it's not server-side rendering to
           | me.
           | 
           | On the other hand, if I am paying for the overhead of running
           | a node server to host my site, but it doesn't support this,
           | why not? I'm very suspicious of the argument that it's to
           | save time due to on-the-fly render costs.
           | 
           | It seems that Remix and perhaps React's new server-side
           | components will be solving this problem in a much more
           | coherent way without requiring weird hacks like this.
        
             | technobabbler wrote:
             | In the Next.js/Vercel world, this hybrid architecture is
             | primarily (I believe) an issue of cost savings (for them
             | and you). For $20/mo Vercel will host your hybrid Next.js
             | side, yet do all of the backend configuration and
             | maintenance on your behalf, configuring not just NPM but
             | Lambda and Cloudflare Workers, along with nginx and caching
             | and CDN mirroring and invalidations, all seamlessly.
             | 
             | So as a dev you can just code against the Next.js docs and
             | not have to play devops. That's a LOT of time savings for
             | $20/mo, and you can afford way more page hits this way than
             | a barebones $20/mo NPM VM would get you. Yes, backend it's
             | super complex and fanned out to different providers, but
             | Vercel manages all of that for you.
             | 
             | I imagine Gatsby and Netlify are similar, but not sure.
             | Next.js was designed around Vercel specifically (sadly) and
             | there's a lot of vendor-specific lock-in features.
        
               | mjlawson wrote:
               | I completely get that this ease of configuration is a
               | huge selling point. Yet I don't think that precludes the
               | possibility of pre-loading information and injecting it
               | at runtime. My main qualm here is probably one where the
               | meaning behind terms like "server-side rendering" are
               | drifting to favor artificial limitations imposed by
               | framework providers.
               | 
               | > Next.js was designed around Vercel specifically (sadly)
               | and there's a lot of vendor-specific lock-in features.
               | 
               | I ran into this while playing around with their
               | middleware. It surprised me that native Node APIs weren't
               | supported which significantly diminishes its utility.
        
               | technobabbler wrote:
               | > Yet I don't think that precludes the possibility of
               | pre-loading information and injecting it at runtime.
               | 
               | Can you explain?
        
       | technobabbler wrote:
       | I think the common, simpler way is to make the component itself
       | (<AuthenticatedNav>) state-aware. So it always returns that
       | component, but the component itself renders differently
       | (`isLoggedIn && <NavBarSubComponent>`) depending on some upstream
       | state passed as a prop or a shared context via a context
       | provider.
       | 
       | If you return two different tags like the author did, React won't
       | know how to properly inject that into the DOM. They are different
       | elements, after all.
        
       ___________________________________________________________________
       (page generated 2021-12-02 23:03 UTC)