@ddlab/renderer
npm i @ddlab/renderer
This lib was written for educational purposes only. Not recommended for production use.
A tool to build user interfaces with javascript (or typescript) in a declarative fashion.
Features:
- Typescript declarations
- JSX support
- uses virtual DOM (no unecessary DOM updates, etc.)
- component state management (
withState()
) - simple hash router (
withRoutes()
)
Usage
Getting Started
The very basic example:
import {h, bootstrap} from '@ddlab/renderer';
const MyComponent = () => h.div([
h.h1('Welcome'),
h.p('to the party')
]);
bootstrap(document.body, MyComponent);
The same example in JSX (see JSX setup section):
import {h, bootstrap} from '@ddlab/renderer';
const MyComponent = () => (
<div>
<h1>Welcome</h1>
<p>to the party</p>
</div>
);
bootstrap(document.body, MyComponent);
Bootstrapping
Bootstrap the RootComponent
to an element
using bootstrap(element, RootComponent)
function. E.g.:
import {h, bootstrap} from '@ddlab/renderer';
const Home = () => h.div('Hello world!');
bootstrap(document.body, Home);
Components
Abstract out your app's UI elements to components and compose out of it. E.g.:
import {h, bootstrap} from '@ddlab/renderer';
const Header = () => h.h1('Welcome');
const Content = () => h.div('This is content.');
const Home = () => h.div([Header(), Content()]);
bootstrap(document.body, Home);
Attributes
The first argument accepts the attributes
object, i.e. h.div(attributes, 'content...')
. E.g.:
const style = {color: 'white', backgroundColor: 'blue'};
h.div({class: 'content', style}, 'content...');
* camelCase keys in style
object will be converted to kebab-case during rendering.
DOM Events
h.div({
onClick: e => console.log(e),
onKeypress: e => alert(e.key),
}, 'content...')
Hooks
There are few component specific events (hooks):
onCreate()
- called on component creationonRemove()
- called on component removalonUpdate()
- called on props/contents updateshouldUpdate(newConfig, oldConfig)
- when it returns false
, it prevents the component from being rerendered
E.g.: h.div({onCreate: () => console.log('hello world!')}, 'content...')
Dynamic Content (using props
)
const Counter = (props) => h.div(`#${props.count} visits.`);
const Home = () => h.div([
Counter({count: 99})
]);
State Management (using withState()
)
const StatefulComponent = withState(Component)
- withState is a hoc which wraps a Component
in a returned StatefulComponent
.
Wrapping a Component
with setState(Component)
adds state
and setState
to the Component
's props. Calling setState(changes)
updates the state
object and rerenders the Component
. E.g.:
import {h, withState} from '@ddlab/renderer';
const StatefulCounter = () => withState(p => h.div([
h.div(`#${p.state.count} clicks.`),
h.button({
onClick: () => p.setState(p.state.count + 1)
}, 'Click me')
]));
Router (using withRoutes()
)
withRoutes()
is a hoc which passes different children for different location hashes to a target component. Example of router configuration:
import {h, withRoutes} from '@ddlab/renderer';
const Home = () => h.div('Home page');
const About = () => h.div('About page');
const RootComponent = withRoutes(h.div, {
'home': Home,
'about': About
});
Now setting hash in address bar to '#home'
loads Home
component & About
component for '#about'
.
Force rerender
Examine withState
& withRoutes
source to see how it works.
Type Safety
Use built-in TComponent<TProps>
type, e.g.:
import {h, TComponent} from '@ddlab/renderer';
interface ICounterProps {count: number};
const Counter: TComponent<ICounterProps> = (props) =>
h.p(`#${props.count} visits.`);
JSX Setup
Set jsx factory function to 'h'
. It is still important to import the h
function in your jsx/tsx files.
Example tsconfig:
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h"
}
Known Issues
withState()
uses the same state object for all instances.