diffHTML

v1.0.0-beta.29

An easy-to-use Virtual DOM built for the web!


Components

Components are used to organize and reuse common parts of your UI. They can be defined as either classes or functions. You may register any component as a Web Component.


Overview

Components support is provided by a plugin and must be installed and imported before they will work. The middleware provided by the plugin supports functions, classes, and web components.

npm install --save diffhtml-components


Function component

Functions are a simple and powerful way of creating UI components that allow you to focus on incoming properties and returning markup. They are stateless and require the parent component to re-render before they will update.

import { html, innerHTML } from 'diffhtml';
import 'diffhtml-components';

function MyComponent(props) {
  return html`
    <div>Some prop = ${props.someProp}</div>
  `;
}

innerHTML(document.body, html`<${MyComponent} someProp="value" />`);

You can make them stateful though using hooks inspired by React. The currently available hooks are:


Class Component

The stateful class component, which is used by importing the Component class.

import { Component } from 'diffhtml-components';

Once you have this base class, you can extend your ES6 class to get access to sub-tree rendering. A component render result is treated as a fragment, and are compared with previous results. All top-level elements are tracked and efficient diffing is applied against the previously rendered contents.

import { innerHTML, html } from 'diffhtml-components';
import { Component } from 'diffhtml-components';

class ListComponent extends Component {
  render({ items }) {
    return html`
      ${items.map(item => html`
        <li>${item}</li>
      `)}
    `;
  }
}

innerHTML(document.body, html`
  <ul>
    <${ListComponent} items=${['List item 1', 'List item 2']} />
  </ul>
`);

These components can be registered as Custom Elements, making them Web Components.

Props

Incoming attribute values which are mapped into a props object, children is a special property which is provided that maps to the VTree childNodes. You can access props on this.props or in the render(props) {} method.

State

A mutable object that can be updated with setState. You can also manually modify this object and call forceUpdate to simulate what setState does. This notion of state is what makes a component reactive. Without it, components only re-render when their parent has rendered.

setState

This is the most common way of updating state that is local to a component. You use it to update the state object and trigger a re-render.

forceUpdate

Calling this function schedules a re-render of the current component. It is useful to call this when you know the state has changed and want it reflected.

Lifecycle hooks

The following hooks will be called during the respective mounting and unmounting flow. You do not need to extend from Component to use these hooks. Simple classes can just define them as methods and they will be called.

componentDidMount

import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';

class DidMountComponent extends Component {
  render() {
    return html`
      <div><h1>Hello world</h1></div>
    `;
  }

  componentDidMount() {
    console.log('Component has mounted');
  }
}

innerHTML(document.body, html`<${DidMountComponent} />`);

componentDidUpdate

import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';

class DidUpdateComponent extends Component {
  render() {
    return html`
      <div><h1>Hello world</h1></div>
    `;
  }

  componentDidUpdate() {
    console.log('Component was updated');
  }
}

innerHTML(document.body, html`<${WillMountComponent} />`);

componentWillReceiveProps

This method is triggered whenever a component is about to receive props and potentially re-render.

shouldComponentUpdate

This method allows a developer to determine if a component should go through a re-render cycle or not.

componentWillUnmount

This method will trigger right before patching occurs and the component is to be unmounted.


Custom Element

You may register any component as a Custom Element using customElements.define. This will allow you to embed components using a more natural syntax.

import { html } from 'diffhtml';
import { Component } from 'diffhtml-components';

class ClassComponent extends Component {
  render() {
    const { prop } = this.props;

    return html`
      <div>Hello world with prop ${prop}</div>
    `;
  }
}

customElements.define('custom-element', ClassComponent);

Class components must use tagged template interpolation:

html`<${ClassComponent} prop=${value} />`

Where Custom Elements may be referenced like any other HTML element:

html`<custom-element prop=${value} />`


createState

The function createState is used to make a function component stateful, which is the ability to re-render itself. It mimics the API of useState from React.

Examples

Simple example demonstrating an incrementing counter.

import { innerHTML, html } from 'diffhtml';
import { createState } from 'diffhtml-components';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [ count, setCount ] = createState(0);

  return html`
    <div>
      <p>You clicked ${String(count)} times</p>
      <button onClick=${() => setCount(count + 1)}>Click me</button>
    </div>
  `;
}

innerHTML(main, html`<${Example} />`);


createSideEffect

The function createSideEffect is used to schedule some work after a component has mounted, unmounted, or updated. This works similar to the useEffect hook found in React and maps directly to the class lifecycle hooks: componentDidMount, componentDidUpdate, and componentWillUnmount.

Examples

import { innerHTML, html } from 'diffhtml';
import { createSideEffect } from 'diffhtml-components';

function Example() {
  createSideEffect(() => {
    console.log('Component has mounted');

    return () => {
      console.log('Component has updated');
    };
  }, () => {
    console.log('Component has unmounted');
  });

  return html`
    <div>Hello world</div>
  `;
}

innerHTML(main, html`<${Example} />`);


JSX

JSX is supported out-of-the-box. You will need to configure your compiler to use createTree, or alias it to h or whatever is expected.

import { createTree as h } from 'diffhtml';

function SomeComponent() {
  return (
    <>
      <div>Using JSX</div>
      <span onClick={() => console.log('clicked')}></span>
    </>
  );
}
Edit on GitHub