React: The great Abstraction

React + JSX was launched at JSConf in 2013, I was one of the fortunate Javascript developers to attend the conference, and I will say the reaction and response on the initial announcement was pessimistic at best. The JS nation revolted against this XML like syntax in their JS for like 6 months, then the rest is history. React has grown into the most popular front-end library in the community.

React the planet! 🌎

Hack the planet was the nerd rallying cry in the movie Hackers 1995

Over the last 8 years, we have seen React implemented in just about every presentation layer you can think of, mobile, tvs, terminal, etc. We have also seen React implement music, animations, and more. React creates a declarative composable model on top of Javascript.

Why not servers? 🖥️

There is a project called React-Nil, this project simply lets you create react components that return null. What? What good does that do? Well, this allows developers to use React on the server.

Mad Science 🧬

Disclaimer: I have no idea if this is a good idea or not, but lets have some fun! 🤓

What if we could use React to express our server API?

render(
  <App>
    <Request path="/" handler={hello} />
    <Listen port={3000} />
  </App>
)

Why not? 🤷

With React-Nil we can!

In this demo we are using Deno 1.8 with Opine web framework

create new folder called :hyper-sauce

mkdir hyper-sauce
cd hyper-saurce
touch hyper.js
touch mod.jsx
touch import_map.json

setup our dependencies in import_map.json

{
  "imports": {
    "react": "https://cdn.skypack.dev/react?dts",
    "react-nil": "https://cdn.skypack.dev/react-nil?dts",
    "opine": "https://deno.land/x/opine@1.1.0/mod.ts"
  }
}

modify hyper.js with our server components

import React from 'react'
import opine from 'opine'

export function Listen({app, port}) {
  app.listen(port)
  console.log('server listening on port ', port)
  return null
}

export function Request({app, method='get', path="/", handler}) { 
  app[method](path, handler)
  return null
}

pretty cool, we are writing react functional components

export function App(props) {
  const app = opine()
  return React.Children.map(props.children, child => {
    if (React.isValidElement(child)) {
      return React.cloneElement(child, { app })
    }
    return child
  })
}

save hyper.js

mod.jsx

NOTE: Deno automatically handles JSX or TSX, just name your file with .jsx or .tsx extension. ✨
import React from 'react'
import { render } from 'react-nil'
import { App, Request, Listen } from './hyper.js'

render(
  <App>
    <Request path="/" handler={hello} />
    <Listen port={3000} />
  </App>
)

function hello(req, res) {
  res.send('Hello from React on the Server!')
}

Let's run it!

deno run --allow-read --allow-net --import-map=./import_map.json mod.js

Open up a browser and navigate to http://localhost:3000

You should see 'Hello World'!

One step further

How about we take this a step further, and instead of passing a handler function, lets use a child component.

render(
  <App>
    <Request path="/">
      <Show />
    </Request>
    <Request path="/_add" method="POST">
      <Inc />
    </Request>
    <Request path="/_sub" method="POST">
      <Dec />
    </Request>
  </App>
)

We can modify the Request component to pass a handler function to the child components.

export function Request({app, method="get", path="/", children}) {
  return React.Children.map(children, 
    child => {
      if (React.isValidElement(child)) {
        return React.cloneElement(child, { 
          handler: fn => app[method](path, fn)
        })
      }
      return child
    })
}

Then we can implement our handlers using components:

function Hello({handler}) {
  handler((req, res) => res.send('Hello!'))
  return null
}

You can run the full demo at https://github.com/hyper63/react-server-demo.git

Summary

React is a powerful abstraction, with context, hooks, suspense, and so much more, it could possibly be useful on the server, with the React-Nil library we can experiment and push the boundaries and see what shakes out!