This is just a thought experiment, but what if NodeJS's http had a baby with push states...
- Add script
<script src="https://raw.githubusercontent.com/cblanquera/browser-http/main/dist/browser-http.js"></script>
- Add this somewhere in your script
const server = http.createServer(function(req, res) { ... });
server.listen();
req
is an extended version of the native window.Request.res
is an extended version of the native window.Response.- This means you can fetch a request as in
fetch(req)
to get info from the server. - This also kind of makes it possible to have a client side
expressjs
framework. - It's possible to make this work as a React Router alternative like the following.
So here we create a very small plugin to use in the main http.Server
handler.
//define a basic react router plugin
//(we'll try to avoid JSX for now...)
const reactPlugin = function(root) {
function isReactElement(element) {
return element && element.$$typeof === Symbol.for('react.element')
}
//this will be populated in a bit...
const plugin = {}
const NotFound = function() {
return React.createElement('div', {}, '404 - Not Found')
}
//main react component
const Response = function() {
//let's use a state hook
const [body, setBody] = React.useState(NotFound())
//a listener needs to go here
//because state hooks cannot be
//used outside of a react component
plugin.handler = (req, res) => {
if (isReactElement(res.body)) {
setBody(res.body)
res.body = null
} else {
setBody(NotFound())
}
}
//return element here
return React.createElement('div', {}, body)
}
//go ahead and render the react to the root
if (typeof root === 'string') {
root = document.getElementById(root)
}
ReactDOM.render(React.createElement(Response), root)
//return the plugin
return function(req, res) {
if (typeof plugin.handler === 'function') {
plugin.handler(req, res)
}
}
}
Next we want to create some pages.
const page1 = function(req, res) {
const App = function() {
const [counter, setCounter] = React.useState(600)
React.useEffect(() => {
const interval = setInterval(() => setCounter(counter => counter - 1), 1000)
return function() {
clearInterval(interval)
}
})
return React.createElement('h1', {},
'Countdown ',
counter
)
}
res.body = React.createElement(App)
}
const page2 = function(req, res) {
const App = function() {
const [counter, setCounter] = React.useState(0)
React.useEffect(() => {
const interval = setInterval(() => setCounter(counter => counter + 1), 1000)
return function() {
clearInterval(interval)
}
})
return React.createElement('h1', {},
'Countup ',
React.createElement(CounterText, { counter })
)
}
const CounterText = function({ counter }) {
return React.createElement('span', {}, counter)
}
res.body = React.createElement(App)
}
Last thing is to add it to the main http.Server
handler.
const plugin = reactPlugin('root')
http.createServer(function(req, res) {
const url = new URL(req.url)
//make a router
switch(url.pathname) {
case '/foo/bar':
page1(req, res);
break;
case '/bar/foo':
page2(req, res);
break;
}
plugin(req, res)
}).listen()
More examples can be found in the example folder in this repo.