2017-02-08 User interfaces as pure JSON data

Sometimes it is practical if the user interface is a pure JSON data structure:

  • it makes it easy to serialise it – for example for moving it between rendering thread and vie thread
  • would trivially make it possible to show the UI on different devices at the same time, etc.
  • you can use existing tools for working with the data, – for example immutable Object/Array implemenations for fast diffing etc.
  • useful for using react in environments without JSX
  • pure data are easier to reason about

This is needed when making https://appedit.solsort.com. I do not want to reinvent the wheel, so I build upon the following ideas:

  • re-frame, and redux has a good mental model of how user interfaces works, – notice that the views just emits events.
  • React have the framework and community around rendering the components
  • HTML is already data – mostly except for event handling. HTML serialises nicely into JSON as JsonML.
  • Reagent is a very similar to what I want.

So what I do is:

  • JsonML data structures are rendered via React. Special syntax for requiring components on the fly.
  • Event handlers are data descriptions of how to transform event into a message that can be dispatched. The event handling and message passing between processes is done via https://direape.solsort.com.

An example of a UI data structure is:

    {"onClick": {"solsortEvent": 
      {"name": "hi",
       "pid": "PID6aih4gsogfj3gmfou82qof75k53da7fg",
       "extract": ["clientX", "clientY"]}}},
    "Click me"],
  ["react-star-rating:default", {"name": "star-rating", "rating": 5}]]

The {"solsortEvent": ... } describes how to handle events, – in this case extract clientX and clientY of the event and send it as a message to a mailbox name hi, in the process PID6ai...a7fg. The react-star-rating:default tag, refers to the react-component require('react-star-rating').default, and can be used to load react components directly from npmjs.

In practice I have utility functions for making event objects etc., so a full UI example could be:

var ss = require('solsort');
    ['button', {onClick: ss.event('hi', {extract: ['clientX', 'clientY']})},
      'Click me'],
    ['react-star-rating:default', {name: 'star-rating', rating: 5}]]);
ss.handle('hi', e => console.log('hi', e));

The ss.event automatically sets the target for the event message to the current thread. Thus when another thread reads and renders the UI data structure, the events end up in the right thread. Clicking on the button prints ‘hi’, and the coordinates of the click. ss.html saves the UI as a part of the state for the current thread, which other thread can subscribe to reactively.