/dom

Experimental React-like library to build DOM

Primary LanguageJavaScriptMIT LicenseMIT

DOM Library

📚 Documentation | 🎉 Example | 🌐 Internet modules

  • ⚠️ 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)

⚠️ 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 ⚠️⚠️
    • 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


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'



📝 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)



🎥 render(VNode, parent)

Renders a virtual node and mount-it into a parent Element

⚠️ 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!

⚠️ 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)
})



📙 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])

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




💾 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)



🔍 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)