From React to Web Components: using mobx
Tags: react, webdev, webcomponents, mobx
Published: 2019-12-12 21:23:29

[Mobx](https://mobx.js.org/README.html) is a popular JavaScript state management library. Most of its documentation and examples revolves around React but at its core is framework agnostic and can be used for any web application.

I looked if and how i could use Mobx together with web components/LitElement, my favorite client side view stack.

Since simple, "todo app" like, demos do not show the full benefits, and possible drawbacks, of such technology, i decided to convert [RealWorld React/mobx](https://github.com/gothinkster/react-mobx-realworld-example-app) demo, a reasonably complex application.

## The React Escape Route

The conversion from React to LitElement is not technically difficult, but is bit annoying since many of it must be done manually. Below are the steps to get the job done:

### Build setup

The original app is built with Create React App which, obviously, does not work for web component project. A custom (minimal) webpack setup was used instead. The most complex part is to configure decorator support in babel:

```js
{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }]
  ]
}
```

### HTML Rendering

LitElement uses `html` tagged template instead of JSX language for HTML rendering. The concepts are similar and seasoned React developers should not have issues making the transition. This [tool](https://blikblum.github.io/to-htm/web/dist/) can automate most of the conversion.

JSX:

```js
<ul className="error-messages">
  {
    Object.keys(errors).map(key => {
      return (
        <li key={key}>
          {key} {errors[key]}
        </li>
      );
    })
  }
</ul>
```

lit-html:

```js
html`
  <ul class="error-messages">
    ${Object.keys(errors).map(key => {
      return html`
        <li key=${key}>${key} ${errors[key]}</li>
      `;
    })}
  </ul>
`;

```

### Mobx Integration

Mobx integrates with React through mobx-react package. It provides a way to re-render components on state change (`observer` decorator) and a specialized dependency injection system to expose state to components (`Provider` component + `inject` decorator).

For LitElement, [lit-mobx](https://github.com/adobe/lit-mobx) provides a way to render components on state change and [wc-context](https://github.com/blikblum/wc-context) is used to expose the state to components.

### Routing

Personally, i prefer to define the app router / routes in a separate data structure (out of component tree), but for sake of simplicity [@stencil/router](https://github.com/ionic-team/stencil-router) is used because its API is very similar to react-router one. Update: replace by [slick-router](https://github.com/blikblum/slick-router), a smaller and more powerful solution.

### Testing

The original demo does not have tests but it was something i really wanted to see how would work. I choose the test setup provided by [open-wc](https://open-wc.org/testing/#setup) which uses karma with a configuration tailored for web components and ES modules. The open-wc testing helpers are really helpful, something to look forward. Unfortunately, the setup breaks with third party packages distributed as commonjs modules without an effective workaround. Update: by using import-map support, i managed to get tests working.    


## Conclusion

Mobx works well with LitElement. There was no need to change the state management code at all to get the demo working. Also the Mobx / LitElement integration through lit-mobx works as expected, no issues founds in this aspect. 

The converted code can be found [here](https://github.com/blikblum/lit-element-mobx-realworld-example-app) alongside a [live demo](https://blikblum.github.io/lit-element-mobx-realworld-example-app) and the [bundle size composition](https://blikblum.github.io/lit-element-mobx-realworld-example-app/bundle-size.html).

### Some notes

 - [mobx-devtools](https://github.com/mobxjs/mobx-devtools) works with a web component app! 
 - People should really analyze the bundle output. The Promise.prototype.finally shim increases bundle size by 43Kb (minified) due to a dependency from which only [three functions are used](https://github.com/es-shims/Promise.prototype.finally/issues/21).
 - @stencil/router did the job but i would not recommend for non stencil projects since it brings together the stencil stack adding 62Kb of minified code. Also it lacks advanced features.
 - Mobx implements only the "legacy" decorator spec, so any project that uses the new spec is out of luck. Not Mobx fault at all because the [decorator](https://github.com/tc39/proposal-decorators) state is a great mess at this moment.
 - Testing web components apps is a Far West like world without an out of box solution. [open-wc setup](https://open-wc.org/testing/#setup) works only with projects where all dependencies are ES modules, something not realistic in many cases (like that). [Jest](jestjs.io) does not support web components at all (and have no signs of doing so). The devs are left in the wonderful world of configuring karma with webpack / mocha / chai etc.