2020-04-06

33 line React - thoughts

Original post, original discussion.

There were lots of insightful comments in the hacker news thread - thanks, I thought I'd write up some of the thoughts that came out of it. Looking now, this may just be a rant piece - I'll let you decide. In a vaguely Top-Gear-esque way, this post is split into style, performance, conclusions.

TLDR

Have a go building your next frontend with an as simple as possible pageState -> DOM model, maybe use mithril.

.jsx, state management and aesthetics

The React homepage, has the following snippet:

class Timer extends React.Component {
    constructor(props) {
        super(props);
        this.state = { seconds: 0 };
    }
    tick() {
        this.setState(state => ({
            seconds: state.seconds + 1
        }));
    }
    componentDidMount() {
        this.interval = setInterval(() => this.tick(), 1000);
    }
    componentWillUnmount() {
        clearInterval(this.interval);
    }
    render() {
        return (
            <div>
                Seconds: {this.state.seconds}
            </div>
        );
    }
}

Versus, for example, the code for the noughts and crosses in my original post, there's a huge amount of ceremony here. I have to:

There are alleged performance/codebase management reasons for some of these, but I remain a bit sceptical of their applicability to "normal" sized web applications.

Ezz-thetic

To my eyes, the original mithril TodoMVC source is exceptionally expressive and handsome, especially versus the equivalent React example. Maybe I'm turning into Arthur Whitney, but I'm kind of enjoying long, dense lines like:

m("section#main", {style: {display: state.todos.length > 0 ? "" : "none"}}, [
    m("input#toggle-all[type='checkbox']", {checked: state.remaining === 0, onclick: ui.toggleAll})
    ...

Consider the recommended React/JSX equivalent:

if (todos.length) {
    main = (
        <section className="main">
            <input
                id="toggle-all"
                className="toggle-all"
                type="checkbox"
                onChange={this.toggleAll}
                checked={activeTodoCount === 0}
            />
            <label
                htmlFor="toggle-all"
            />
            <ul className="todo-list">
                {todoItems}
            </ul>
        </section>
    );
}

Routing

As a consumer of webpages, I'm not sure I've ever seen the URL change in an SPA and thought "phewph, I'm glad I wasn't redirected to a new page" - just gimme a normal <a> and split your app up yo.

Performance

I had a very unscientific play around with this neat benchmarking tool, you can use the "Custom URL"s http://leontrolski.github.io /benchmark, /benchmark/mithril.html, /benchmark/lit-html.html to compare yourself. I'm going to keep the performance figures intentionally vague - I was comparing Vanilla JS, React 16, mithril and 33-line.

Performance - Notes

The Vanilla JS one just shoves strings of html together and does a big container.innerHTML =, nice and simple. On the other hand, string munging sucks for obvious reasons. Also, you end up with a lot of updates flashing around in the devtools inspector.

I had to write a few extra lines of 33-line to handle data- attributes, that cranked it up to 37 lines. I think if you were to try productionise this toy code you'd end up with about 3/4 of a mithril.

I did one run with the stage0 library thingy, the code was a bit more gnarly, but it was rapid. If I was writing eg. a game or a big spreadsheet app that needed high performance, I'd definitely be considering a library in this space.

Things that I'd imagine React particularly excels at versus a naive approach would be things like clock counters/animations - small bits of the DOM changing at high frequency - the tradeoff for this performance seems to be state-management-based API complexity. If one is to use a simpler pageState -> DOM model with few library hooks into the guts, it may be necessary to implement clocks etc. out of band of the normal library's render loop.

Hacker news meta bit

For a while, the top-voted thread was people moaning about how a variable was called m, then a later comment in the code said it was a grid. I agree it was maybe a bit annoying, but I dunno, you read the article and that was your takeaway.. I've been part of a fair few code reviews with this vibe in my time :-)

Conclusions

Doing document.querySelectorAll('*') on the airbnb map view (a reasonably complex SPA) returns 3407 elements.

With no thought to performance at all, a simple library can render in the order of 100s of divs per millisecond. You could probably swap React out with 33-line on most sites and no-one would notice, you could also swap it out with some Vanilla JS string munging too - although the developer egonomics would be a bit rubbish.

In their next project, I'd recommended any frontend devs out there embrace there inner minimalist and cut the fat. Make a plain ol' state variable at the top of your file, throw in some functions that mutate it, a touch of mithril to render it, and bang, you're done.

If your site's slow (unless you're something really complicated like a game/spreadsheet), it's probably that you put a lot of crap on it, rather than anything to do with how you render your divs.