[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)