@internet/dom
v1.3.0
Published
Experimental React-like library to build DOM
Downloads
3
Readme
DOM Library
:books: Documentation | :tada: Example | :globe_with_meridians: Internet modules
- :warning: Experimental React-like library to build DOM, without
setState
and patching. - Own JSX implementation using
h
pragma. render
method to render and mount jsxcloneElement
to clone a virtual node with new props or new children- Class
Component
with react-like lifecycle methods. - Callback refs support.
- You can also render "real" HTML Elements inside virtual dom
- This is useful to add a Component-oriented architecture on top of an already existing html page (like rendered from PHP)
:warning: Warnings
This is not a React alternative, use preact for this purpose.
You can render "real" HTML Element inside virtual dom (using
render
orcomponent.render
)- It's the one of the reasons why there isn't patching
- It's a great feature to add a Component-oriented architecture on top of an already existing html page (like rendered from PHP)
- It also means that the virtual dom is absolutely not a source of thruth :warning::warning:
- It can be super easy to have leaks and a bad lifecycle behaviour, so don't rely too much on this lib
There is no event management for now. Use
addEventListener
/removeEventListener
with lifecycle methods to be sure of what you are doing.render
have different arguments than the preact / React one.component.render
is used to render portions of jsx inside it, as child of thecomponent
- the initial rendering of the component is made via the
component.template
method instead
- the initial rendering of the component is made via the
Requirements
- ES6 Modules support
- Using a module bundler like Webpack, Rollup or Parcel
- Native support from browser
- From NodeJS with something like esm
Installation
# using npm
$ npm install --save @internet/dom
# or using yarn
$ yarn add @internet/dom
API
import { h, render, Component, cloneElement, addRef } from '@internet/dom'
- :pencil: h(): Creates a VNode (usually used through JSX)
- :movie_camera: render(): Renders a virtual node and mount-it into a
parent
Element - :orange_book: Component Class: Base Component class with lifecycle and rendering methods
- :floppy_disk: cloneElement(): Clones the given virtual node, optionally replacing its props / children
- :mag: addRef(): Quick util to add callback refs to jsx
:pencil: h([tag|Component], [props={}], ...children)
Creates a VNode (virtual DOM element).
Kind: global function
Returns: object - A vnode object containing its type, props, children and some flags
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| nodeName | string | function | | A tagname or a component. e.g. 'div'
, 'a'
, Component
|
| [props] | object | {} | Attributes/props to set on the created element. |
| [...children] | Array.<VNode> | [] | All additional arguments are vnode children |
Example
import { h, render } from '@internet/dom'
// Create a new vnode using JSX (via Babel or Bublé)
const node = <div style="color: red">Hello</div>
// The same node can be made using directly the `h` function
const node = h('div', { style: 'color: red' }, 'Hello')
// render the created node into <body>
render(node, document.body)
:movie_camera: render(VNode, parent)
Renders a virtual node and mount-it into a
parent
Element
:warning:
render
always dispatch lifecycle events. Even if you don't pass a parent as 2nd argument, all componentDidMount methods and callback refs will be called. Be carreful!
:warning: If you render a virtual node inside an already mounted component, use its component.render method instead. Otherwise, the rendered subcomponents and callback refs will not be register as "childs" of the parent component. This can lead to bad lifecycle dispatching if you destroy the parent for instance.
Kind: global function
Returns: object - Return an object containing:
result.nodes
: Array of rendered DOM nodesresult.components
: First-level components instancesresult.refs
: First-level callback refs
| Param | Type | Description |
| --- | --- | --- |
| vnode | object | A (JSX) VNode to render |
| [parent] | HTMLElement | function | DOM Element to render into. You can also use a callback function: the function will be called with DOM Elements to mount as first argument |
| [parentComponent] | Component | The parent component instance where the vnode will be mounted. You can directly use parentComponent.render(vnode, parent)
|
Example
import { h, render } from '@internet/dom'
import App from 'components/App'
// Instanciate an App component and append it to document.body
render(<App />, document.body)
// Insert a new div into document.body, before the first child of document.body
render(<div>Some text</div>, div => {
document.body.insertBefore(div, document.body.firstChild)
})
:orange_book: Component
class
Example
import { h, Component, render } from '@internet/raf'
class App extends Component () {
template () {
return (
<div>
<p>My first app</p>
</div>
)
}
componentDidMount () {
console.log('App is mounted')
console.log('HTMLElement of the App: ', this.base)
}
}
// Mount a new instance of App component into document.body
// Will call componentWillMount, template and componentDidMount lifecycle events
render(<App />, document.body)
Component API
- Component
- new Component([props])
- Methods
- Properties
new Component([props])
Create a new Component
| Param | Type | Default | Description | | --- | --- | --- | --- | | [props] | object | {} | Component properties / attributes. Can also contains children. |
component.template([props]) ⇒ VNode | HTMLElement
component.template
will be called during the component initial rendering to create thecomponent.base
node
Kind: instance method of Component
Returns: VNode | HTMLElement - VNode (JSX or h
calls) or real HTMLElement that will be rendered as the component.base
node. You can also return an array of elements.
Category: Methods
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [props] | object | {} | component.props
automatically passed as argument. |
Example
import { h, Component, render } from '@internet/dom'
class HelloDiv extends Component {
template () {
// will create a new p tag
return <div>Hello!</div>
}
}
// Append a new "Hello!" div to document.body
render(<HelloDiv/>, document.body)
class MainComponent extends Component {
template () {
// use the existing <main> node as the component base
return document.querySelector('main')
}
}
// Create a new MainComponent, using an already existing dom node
render(<MainComponent />)
component.componentWillMount([props])
component.componentWillMount
will be called byrender
just before a the component template rendering
Kind: instance method of Component
Category: Methods
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [props] | object | {} | component.props
automatically passed as argument. |
component.componentDidMount([props])
component.componentDidMount
will be called byrender
when all the rendered dom tree is mounted
Kind: instance method of Component
Category: Methods
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [props] | object | {} | component.props
automatically passed as argument. |
component.componentWillUnmount([props])
component.componentWillUnmount
will be called when the component or one of its ancestors is destroyed
Kind: instance method of Component
Category: Methods
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [props] | object | {} | component.props
automatically passed as argument. |
component.render(vnode, [parent]) ⇒ object
Render a vnode or array of vnodes and register the rendered content as "child" of this component. Use this method when you want to add content to the component after the initial rendering. This ensures new items will be correctly unmount when the component is destroyed.
Kind: instance method of Component
Returns: object - Return an object containing rendered nodes
, components
and refs
Category: Methods
| Param | Type | Description | | --- | --- | --- | | vnode | object | A (JSX) VNode to render | | [parent] | HTMLElement | function | DOM Element to render into. You can also use a callback function: the function will be called with DOM Elements to mount as first argument |
Example
import { h, Component, render } from '@internet/dom'
class Item extends Component {
template () {
return <li>Item</li>
}
}
class List extends Component {
template () {
return (
<div>
<ul ref={el => { this.ul = el }}></ul>
<button ref={el => { this.button = el }}>Add Item</button>
</div>
)
}
addItem () {
// Render a new Item instance and add it to the list
// All created Items will be properly destroyed when the List instance is removed
this.render(<Item />, this.ul)
}
componentDidMount () {
this.addItem = this.addItem.bind(this)
this.button.addEventListener('click', this.addItem)
}
componentWillUnmount () {
this.button.removeEventListener('click', this.addItem)
}
}
render(<List />, document.body)
component.destroy()
Destroy the component and its children components.
- This also removes component props and de-reference the component from its parent.
- Callback refs inside the component tree will be called with
null
as first argument - Set component.mounted to false
Kind: instance method of Component
Category: Methods
Example
import { h, Component, render } from '@internet/dom'
class SelfDestructButton extends Component {
template() {
return <button>Destroy me</button>
}
componentDidMount() {
this.destroy = this.destroy.bind(this)
this.base.addEventListener('click', this.destroy)
}
componentWillUnmount() {
this.base.removeEventListener('click', this.destroy)
}
}
render(<SelfDestructButton />, document.body)
component.props : object
Contains all component properties and children. Do not modify it directly, but recreate a new component using
cloneElement
instead
Kind: instance property of Component
Category: Properties
component.base : VNode | HTMLElement | array
HTMLElement used as "base" for the component instance. Can also be an array of elements if
template
return an array.
Kind: instance property of Component
Category: Properties
component.mounted : boolean
Set to true when component is mounted
Kind: instance property of Component
Category: Properties
:floppy_disk: cloneElement(VNode, [newProps={}], [newChildren])
Clones the given virtual node, optionally updating its props and replacing its children
Kind: global function
Returns: object - A new vnode object containing updated props / children
| Param | Type | Default | Description | | --- | --- | --- | --- | | vnode | object.VNode | | A virtual node object to clone | | [props] | object | {} | Attributes/props to set on the created element. | | [...children] | Array.<VNode> | [] | All additional arguments are vnode children |
Example
import { h, render, cloneElement } from '@internet/dom'
const useRed = (vnode) => cloneElement(vnode, { style: 'color:red;' })
const normalText = <p>Some text</p>
const redText = useRed(normalText)
render(redText, document.body)
:mag: addRef(obj, refName)
Create a callback ref function
Kind: global function
Returns: function - A callback ref function
| Param | Type | Description |
| --- | --- | --- |
| obj | object | function | Object or Component instance to add the reference to |
| refName | string | Name of the reference. Will be accessible from obj[refName]
|
Example
import { h, render, addRef, Component } from '@internet/dom'
class App extends Component () {
template () {
return (
<div>
<button ref={addRef(this, 'button')}>A button</button>
</div>
)
componentDidMount () {
console.log('Button is mounted:', this.button)
}
}
}
render(<App />, document.body)