2020-04-03

33 line React

Thoughts on reading through the hacker news response. 96 line version with JSX compiler.


React

It also does a load of other crap as well, but we're going to ignore that.

In this post, I'm going to make the smallest React-like thing that can do the above. It's very mithril influenced.

Here are sample applications: calendar picker, snake that use the library.

Lots of the code looks pretty code-golfy - I promise I don't do stuff like this at work, neither should you :-)

Noughts and crosses

We're going to make this noughts and crosses game:

Now let's look at the code to this, you can also just view the page source if you want.

let currentPlayer = 'o'
let winner = null
const g = [['', '', ''], ['', '', ''], ['', '', '']]  // grid

const move = (value, i, j)=>{
    // ... game logic goes here
    renderNoughts()
}

const Cell = (value, i, j)=>m('button.cell',
    {onclick: ()=>move(value, i, j)}, value
)
const Noughts = ()=>m('',
    winner
        ? m('marquee', `winner: ${winner}`)
        : m('h3', `current player: ${currentPlayer}`),
    m('table', g.map(
        (row, i)=>m('tr', row.map(
            (value, j)=>m('td', {class: value}, Cell(value, i, j)))))),
)

const renderNoughts = ()=>m.render(
    document.getElementById('noughts'),
    {children: [Noughts()]},
)
renderNoughts()

Cute, so what's going on?

First we defined some state:

let currentPlayer = 'o'
let winner = null
const g = [['', '', ''], ['', '', ''], ['', '', '']]  // grid

These hold the state of our game, we will mutate them.

const move = (value, i, j){...}

This function makes a move in the game, it takes 'x' or 'o' along with 2 integer coordinates. It will mutate all the state variables to reflect the new state of the game. After that, it calls renderNoughts(), this is a call to rerender the game - but we'll come back to that.

Next we define the functions that return virtual DOMs, Noughts and Cell.

The m(...) calls take:

And return virtual DOM elements, for example, calling Noughts() would return:

{
    tag: 'div',
    attrs: {},
    classes: [],
    children: [
        {
            tag: 'h3',
            attrs: {},
            classes: [],
            children: [
                'current player: x'
            ]
        },
        {
            tag: 'table',
            attrs: {},
            classes: [],
            children: [
                {
                    tag: 'tr',
                    attrs: {},
                    classes: [],
                    children: [
...

Next we make the function renderNoughts(), when you call it, it will call our Noughts function, and attempt to efficiently render the resulting virtual DOM onto document.getElementById('noughts')

How does m work?

Here's the source with and without comments.