@internet/dom

Experimental React-like library to build DOM

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
@internet/dom
1.3.05 years ago6 years agoMinified + gzip package size for @internet/dom in KB

Readme

DOM Library
:books: Documentation | :tada: Example | :globewithmeridians: 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 jsx
  • cloneElement 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 or component.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 the component
- the initial rendering of the component is made via the component.template method instead

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([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)




:moviecamera: 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 nodes - result.components : First-level components instances - result.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)
})




:orangebook: 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

* [new Component([props])](#new_Component_new)
* _Methods_
    * [.template([props])](#Component+template) ⇒ <code>VNode</code> \| <code>HTMLElement</code>
    * [.componentWillMount([props])](#Component+componentWillMount)
    * [.componentDidMount([props])](#Component+componentDidMount)
    * [.componentWillUnmount([props])](#Component+componentWillUnmount)
    * [.render(vnode, [parent])](#Component+render) ⇒ <code>object</code>
    * [.destroy()](#Component+destroy)
* _Properties_
    * [.props](#Component+props) : <code>object</code>
    * [.base](#Component+base) : <code>VNode</code> \| <code>HTMLElement</code> \| <code>array</code>
    * [.mounted](#Component+mounted) : <code>boolean</code>

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 the component.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 by render 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 by render 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





:floppydisk: 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)