Research project to evaluate and implement a working UI library abstraction API *js-surface* aims to be itself a full-featured UI library (using Dyo under the hood) and also provides the possibility to implement real React and also real Preact components


Keywords
api, hooks, react, ui-components
License
LGPL-3.0
Install
npm install js-surface@0.4.36

Documentation

js-surface

Research project to evaluate and implement a working UI library abstraction API js-surface aims to be itself a full-featured UI library (using Dyo under the hood) and also provides the possibility to implement real React and also real Preact components with the same API.

Licence npm version Build status Coverage status

Remark: This README document is just an early draft - totally incomplete yet

Installation

# To download project and dependences
git clone https://github.com/js-works/js-surface.git
cd js-surface
npm install

# To run tests
npm run test

# To run tests with watching
npm run test-watch

# To start the demos
npm run demo

# To build the project
npm run build

# To build and prepare the project for publishing
npm run dist

Introduction

js-surface is a R&D project to find a pragmatic API that can be used as wrapper API for the actual React, Preact and Dyo APIs with the goal to provide a general API that can be used to write Preact, React and lightweight Dyo-based components Be aware that jsSurface is actually only for research purposes, it's currently NOT meant to be used in real-world applications.

First, here's a small demo application to get a glimpse of how components are currently implemented with jsSurface:

Hello world component

// import from 'js-surface/react' if you want to use React
// under the hood (otherwise a completely wrapped verision of
// Dyo will be used)
import { h, component, render } from 'js-surface'

const HelloWorld = component({
  name: 'HelloWorld',

  main({ name = 'World' }) {
    return div(`Hello, ${name}!`)
  }
})

// Also the following short syntax is supported:

const HelloWorld2 = component('HelloWorld', props => 
  <div>Hello ${props.name}</div>
)

render(<HelloWorld/>, 'app')

Simple counter

import { h, component, render, useCallback, useState } from 'js-surface/react'

// 3rd-party general purpose validation library
import * as Spec from 'js-spec/validators' 

const Counter = component({
  name: 'Counter',

  validate: Spec.checkProps({
    optional: {
      initialValue: Spec.integer,
      label: Spec.string
    }
  }),

  main({ initialValue = 0, label = 'Counter' }) {
    const
      [count, setCount] = useState(initialValue),
      onIncrement = useCallback(() => setCount(it => it + 1), [])

    return (
      <div>
        <label>{label}</label>
        <button onClick={onIncrement}>{count}</button>
      </div>
    )
  }
})

render(<Counter/>, 'app')

In case you are using ESLint with eslint-plugin-react-hooks, the linter will not like the usage above (due to the lowercase first letter of function main). That's why the preferred way to define components is the following:

const Counter = component({
  name: 'Counter',

  validate: Spec.checkProps({
    optional: {
      initialValue: Spec.integer,
      label: Spec.string
    }
  }),

  main: CounterView
})

function CounterView({ initialValue = 0, label = 'Counter' }) {
  const
    [count, setCount] = useState(initialValue),
    onIncrement = useCallback(() => setCount(it => it + 1))

  return (
    <div>
      <label>{label}</label>
      <button onClick={onIncrement}>{count}</button>
    </div>
  )
}

Motivation

  • Use the same code to implement React, Preact and js-surface components. Write a component and decide at build time what UI library shall be used (depending on the complexity of your components, it's possible that conditional compilation/inclues/excludes will be necessary)

  • Reacts provides the possibility for a sophisticated validation of the components' properties, which is great. Normally for that purpose a add-on library called "props-types". While it's great that "props-types" is not part of React itself, yet unfortunatelly all the validation function of "props-types" are only meant to be used with React, it's not meant as a general purpose validation library as the signature of the validation functions are very React-specific. This has some advantages of course (maybe shorter error messages and a bit smaller production bundle sizes) but the disadvantage that you cannot just use a general purpose validation library are really heavy. jsSurface on the other hand allows the use of such general-purpose validation libraries - while it recommended to use the jsSurface independent validation library "js-spec".

Current API (not complete yet)

Module "js-surface"

Basics:

  • h(type, props?, ...children)
  • component(componentConfig) or component(name, main)
  • context(contextConfig)
  • render(content, container) - if content is null then unmount
  • Fragment
  • Boundary

Hooks:

  • useCallback(callback)
  • useContext(ctx)
  • useEffect(action, dependencies?)
  • useForceUpdate()
  • useImperativeHandle(ref, getter, inputs)
  • usePrevious(value)
  • useRef(initialValue)
  • useState(initialValue or getInitialValue)

Helper functions for virtual elements and nodes

  • isElement(it)
  • isNode(it)
  • typeOf(element)
  • propsOf(element)
  • cloneElement(element, props, ...children)
  • setInnerHtml(element, html)

Helper functions for children

  • childCount(children)
  • forEachChild(children, action)
  • mapChildren(children, mapper)
  • onlyChild(children)
  • toChildArray(children)
  • withChildren(func)

Project status

Important: This project is in a early alpha state.