MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Tuesday, May 08, 2018

Recently, I've been playing around with this pretty good howto for Reduxless React SSR, which needed a few tweaks to work.

Once things were running, and I could tell in Chrome's network tab that I was receiving good, pre-rendered [by the server] content, I had some difficulty as an SSR n00b figuring out if the client was wasting time re-rendering or not. I take that back -- render was being called on the client in all of my test app's components, though with SSR they seemingly shouldn't, and I wanted to see if...

  1. The DOM was also changing, and
  2. If I c/sh/ould stop that initial render, even if it was "free".*

* In the second point, what I mean by "free" is that if render is called but nothing's changed -- the SSR completely created your initial state -- there's no practical impact on or for the user if the client wastes a few cycles after initial page render. Those wasted cycles are free, so to speak, as there was nothing else to spend them on. Not great, but no practical problem.

I messed around with the React extension in Chrome for a while, and it looked like things were moving quickly/not manipulating DOM after load. Adding SSR to the test app I'd initially written with client-side-only rendering felt like a win. But this exercise also told me I wasn't familiar enough with React's decision-making process for when it'd decide to call render. Why was render wasting cycles? I'd SSR'd, dang it!

Luckily, I bumped into How does React decide to re-render a component? at lucybain.com that discusses exactly that:

A re-render can only be triggered if a componentโ€™s state has changed. The state can change from aย propsย change, or from a directย setStateย change. The component gets the updated state and React decides if it should re-render the component. Unfortunately, by default React is incredibly simplistic and basically re-renders everything all the time.

Component changed? Re-render. Parent changed? Re-render. Section of props that doesn't actually impact the view changed? Re-render. [emph mine -mfn]

Hearing React wasn't exceptionally choosy about when to refire render made me feel better about all the render calls I saw, even after SSR. My components seem to be rendering on the client side, but it was only in the virtual DOM. That would seem to have to happen to compare the client-rolled DOM to the existing SSR'd DOM.

That means when it comes to the client-side render calls after successful SSR, I believe the answers to my above questions are...

  1. No, the client is not changing DOM.
    • Pro tip: If you use hydrate, React'll yell at you in the console if what's rendered from the server is different from what's expected.
  2. If you want to stop that render, you use (duh) shouldComponentUpdate.

NOTE: Check that pro tip out closely. If you mount React with hydrate (a v16 addition), it should yell at you if you don't have DOM that matches what React expects.

Second, um, don't change the name of your components' render calls to hydrate like my, um, my friend did. That's stupid. You hydrate only where you mount React:

//ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.hydrate(<App />, document.getElementById('root'));

The name of your render functions in your components will stay render.


Why does one use truly universal React?

Now even though I've learned to use the magic of renderToString, while I was searching up best SSR practices, I caught James Nelson's Universal React: You're doing it wrong, which referenced these tweets:

Dan Abramovโ€ย @dan_abramov
Replying toย @james_k_nelson

.@james_k_nelson Actually this isnโ€™t even the case. AFAIK Facebook is not currently using React server rendering.

5:19 PM - 20 Mar 2016

And this one:

Christoph Nakazawaโ€ย @cpojer
Replying toย @dan_abramov

@dan_abramov @james_k_nelson http://instagram.comย  uses server rendering for SEO. We haven't found sr useful for real users.

6:41 PM - 20 Mar 2016

For me, the quick take-home is this:

  1. Universal React wasn't worth the proverbial squeeze...
  2. ... in 2016.

I'd like to see updates on those. Does Instagram still use SSR? Does it think that's smart? Is SEO still enough of a use case for the overhead, especially now that at least Google's spider renders JavaScript? Is the SSR done with Universal React?

This also gives you a clue of where we might go for SSR -- not universal React.

Wait, what? Are you pondering what I'm pondering?

If they called them sad meals...

No no! I'm thinking we should look into doing SSR without React a little more closely.


SSR is just string manipulation

Look, SSR is the simplest thing you've ever read more than three articles about. At its core, it's just string manipulation. Let's look at a little node express code from the article I initially referenced:

// render the app as a string
const html = ReactDOMServer.renderToString(<App />);

// inject the rendered app into our html and send it
return res.send(
    htmlData.replace(
        '<div id="root"></div>',
        `<div id="root">${html}</div>`
    )
);

Follow that?

  1. Create a string to represent rendered content.
  2. Have an html page template for your react. (Nothing new here.)
  3. Replace the root element where you'd mount React and put your rendered html string in the middle.

It's that simple. You could use anything to create that html string, not just React running on Express. All that has to happen is that the content in html, once couched in the html page's template, mimics exactly what React would try to make when it renders OR, get this, you could write your React components in a way that they ignore the DOM from SSR and let the SSR'd DOM alone until state is changed on the client.

Make sense?

The most beautiful upshot of this "discovery" is that you can use any server you want for SSR.

Want to create your SSR content for a React SPA with C# and Razor?

Shia says, "Just do it!"

(It really isn't rocket science here -- the advantages of this approach are essentially tantamount to those when you used Apache in 1997. "Hey look! You want static text?! I can make you static text!" Or maybe more like using Classic ASP in 1997. "You want text generated from a database?!! You got text generated from a database!1!" Wow, man. Groovy.)

But I thought Universal was architecturally superior?

I really like the possibility of using the same language on the server as the client. There seem to be lots of obvious, inherent advantages. For one, you can write your business validation logic once, and reuse it on the client & server.

That's beautiful. For example, you almost get client-side validation "for free" when you write good server-side validation. And if I had a quarter for every time server-side validation on a project lagged behind the client (this is so wrong, folks. SOOOOO wrong), I'd have a few bucks.

But there is overhead in fully supporting universal React once you get past the first, obvious wins. And, truth be told, I've slowly moved off of doing any real validation client-side in all but the most network-starved use cases. I love JavaScript, but I'm practical enough to realize that thin clients are the best clients. Have your client ready to handle validation payloads from the server, but don't have any business logic client-side if you can help it.

Once you get past the proverbial Universal siren song and then also realize that SSR is really, at its core, simply what it says -- html rendered on the server-side, not necessarily html rendered with the same complex code as the client, now on the server -- you will finally have the most flexibility for composing the best, most performant React sites.


Random, somewhat related, somewhat timely reading: Redux - Not Dead Yet!

Labels: , , ,


posted by ruffin at 5/08/2018 03:37:00 PM

I've been playing around with React and Server-Side Rendering (SSR), and found a pretty good howto to set things up in this article: Upgrading a Create React App Project to SSR [without Redux].

Here are a couple of tweaks I needed to perform to get the code from the first half of that article running happily...

First, if you get a...

Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` environment variables

... error on Windows, it's pretty simple to fix. Do exactly what it's asking you to do: Type...

set NODE_ENV=development

... in your cmd window ("cmd" as in DOS; Powershell has another format) and profit.

You could also set NODE_ENV in your environment variables permanently, if you wanted, though remember to change to test or production when applicable.

There's also a complaint in the post's comments where {logo} resolves to the infamous [Object object]. Rather than following the instructions from the author in the comments, I ended up putting that into the folder my express static router accessed.

Here's the existing router code:

router.get("*", function(req, res, next) {
    console.log("static router: " + req.originalUrl);
    express.static(path.resolve(__dirname, '..', 'build'))(req,res,next);
});

No changes there. Just looking at that so you know why we're using the build folder.

Then, in that build folder, things look like this for me now:

C:\PROJECTS\REDUXLESS\BUILD
|   asset-manifest.json
|   favicon.ico
|   index.html
|   manifest.json
|   service-worker.js
|
+---images
|       logo.svg                   <<< Here 'tis!!!
|
\---static
    +---css
    |       main.cdc8ad4f.css      <<< all this "static" stuff is from
    |       main.cdc8ad4f.css.map        the` npm run build` call.
    |
    \---js
            main.d3067283.js
            main.d3067283.js.map

There may have been something else I'm forgetting. Ping me if that's the case...

Labels: , ,


posted by ruffin at 5/08/2018 02:27:00 PM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.