Hack to Run React Application inside Service Worker
Tags: javascript, react, hack, http
Published: 2023-01-05 18:53:19

---
title: Hack to Run React Application inside Service Worker
published: true
description: This article will show how I created a React application inside service worker using Wayne and jsDOM libraries
tags: javascript, react, hack, http
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/42yaaav05atmzyn84axz.png
# published_at: 2023-01-05 14:52 +0000
---

I was doing my thing and then suddenly had an enlightenment. A new cool idea came to my mind.

But first a little bit of background. Last year I created a [JavaScript library called Wayne](https://github.com/jcubic/wayne) that abstracts the idea of creating pure in-browser HTTP requests with the help of a Service Worker. The API is similar to the express.js NodeJS framework but works in the browser instead of the server.

So the cool idea that I had was to use a ReactJS application inside Service Worker and return a snapshot of that application from different HTTP requests. I've even been thinking about using HTTP requests to interact with applications.

I've started creating a PoC. My first try was searching for a library that was used for testing the React app. Since you can't use real [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) inside [Service Woker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API). I was looking at [react testing library](https://testing-library.com/docs/react-testing-library/intro/) documentation in the hope that they use some kind of fake DOM library, but after looking at the code it was using real ReactDOM and also it was not very intuitive. They lack getElementByID and were created just for making tests. So I've abandoned the idea of using a testing library.

But then I realized that I should probably need to use [jsDOM](https://github.com/jsdom/jsdom). This is the library that can be used in nodejs to mock the DOM. This is what [jest testing framework](https://github.com/facebook/jest) is using and this is what I was using to test [jQuery Terminal library](https://terminal.jcubic.pl/) in Jasmine before jest was created.

One problem was to run jsDOM as [UMD module](https://jcubic.pl/2017/07/uniwersalne-biblioteki-javascript.html). But luckily I was able to use [browserify](https://browserify.org/) to compile jsDOM into UMD.

Below is the code I've used:

```jsx
import * as React from 'react';
import { createRoot } from 'react-dom';
import { Wayne } from '@jcubic/wayne';
importScripts('https://cdn.jsdelivr.net/gh/jcubic/static/js/jsdom.min.js');
const { JSDOM } = jsdom;

const pathname = location.pathname.replace(/\/sw.js$/, '');

const Nav = () => {
    return (
        <ul>
          <li><a href={`${pathname}/`}>home</a></li>
          <li><a href={`${pathname}/__about__`}>about</a></li>
          <li><a href={`${pathname}/__contact__`}>contact</a></li>
          <li><a href={`${pathname}/source.jsx`}>Service Worker source</a></li>
        </ul>
    );
}

let globalSetPage;

const App = () => {
    const [page, setPage] = React.useState('root');
    React.useEffect(() => {
        globalSetPage = setPage;
    }, [setPage]);
    return (
        <>
          <Nav/>
          <p>Helo React + Wayne "{page}"</p>
        </>
    );
};

const dom = new JSDOM(`<!DOCTYPE html>
<html>
  <head><title>React App</title></head>
  <body>
    <div id="root"></div>
  </body>
</html>`);

self.window = dom.window;
self.document = self.window.document;

const root_node = document.getElementById('root');

const root = createRoot(root_node);
root.render(<App/>);

function html(res) {
    setTimeout(() => {
        const html = `<!DOCTYPE html>${document.documentElement.outerHTML}`;
        res.html(html);
    }, 0);
}

const app = new Wayne();

app.get('/__*__', (req, res) => {
    globalSetPage(req.params[0]);
    html(res);
});

app.get('/source.jsx', async (req, res) => {
    const text = await fetch('../sw.jsx').then(res => res.text());
    res.text(text);
});
```

To explain the code:

1. It first loaded jsDOM from my private CDN using jsDelivr
2. Then create an application with the useState hook
3. It saves the setState from the hook as a global variable
4. It adds Wayne routes and calls the setState function with data from the user

And that's it. To pass data to the app you just need to open URL `__<SOMETHING>__` and the stuff between double underscores will be page variable inside React.

The code assigns the internal set state function into a global variable, but it would probably be better to use something like an EventEmitter for this. But this is just a hack and it works. This also makes the code easier to understand, even if it's not best practice.

You can give this hack a try [here](https://jcu.bi/wayne-react).

The source code can be found on GitHub in [gh-pages branch](https://github.com/jcubic/wayne/tree/gh-pages/docs/jsx).

And that's it. If you like this post, you can follow me on Twitter at [@jcubic](https://twitter.com/jcubic) and visit my [home page](https://jakub.jankiewicz.org/).