Home

Awesome

TUSK (UNMAINTAINED) - See Set-DOM

A lightweight viritual-dom with a friendly interface.

Join the chat at https://gitter.im/DylanPiercey/tusk npm

Why

Many virtual-dom implementations are bulky and are not optimized for immutable data or server side rendering. Currently this is experimental and should not be used in production.

Features

Supports

Installation

Npm

npm install tusk

Bower

bower install tusk

Example

/** @jsx tusk */
let tusk = require('tusk');
// Using immstruct for example, feel free to replace with immutable.js or others.
let immstruct = require('immstruct');
// Define some initial state for the app.
let struct = immstruct({ i : 0 });

function MyCounter (props, children) {
    let { message, cursor } = props;

    // Define handlers.
    let handleClick = (e)=> cursor.update((state)=> state.set("i", state.get("i") + 1));
    let setup = (e)=> ...;
    let teardown = (e)=> ...;

    // Render the component.
    return (
        <body>
            <button onClick={ handleClick } onMount={ setup } onDismount={ teardown }>
                { message } : { cursor.get('i') }
            </button>
        </body>
    );
}

// Initial render
render();

// We can use the render function to re-render when the state changes.
struct.on("next-animation-frame", function render () {
    tusk.render(document.body,
        <MyCounter message="Times clicked" cursor={ struct.cursor() }/>
    );
});

// We can also render into a string (Usually for the server).
let HTML = String(<MyCounter type="Times clicked" cursor={ struct.cursor() }/>);
// -> "<body><button>Times clicked : 0</button></body>"

API

tusk.render(document.body,
    <body>
        <div>Hello World</div>
    </body>
);
// -> document.body.innerHTML === "<div>Hello World</div>"
tusk.isElement(<div/>); // true
// renderer must be a function that returns a virtual node.
function MyComponent (props, children, context) {
    return (
        <div>External data: { context }</div>
    );
}

String(tusk.with(1, ()=> <MyComponent/>));
//-> "<div>External Data: 1</div>"
// Automatically called when using JSX.
let vNode = tusk.createElement("div", { editable: true }, "Hello World");
// Or call tusk directly
let vNode = tusk("div", { editable: true }, "Hello World");

// Render to string on the server.
vNode.toString(); // '<div editable="true">Hello World</div>';

/**
 * @params type can also be a function (shown in example above).
 */

Advanced Performance

Memoization

In React and many other virtual doms "shouldUpdate" is a common theme for performance. Tusk does not feature shouldUpdate and opts for a more performant, simpler, and well known approach: memoization.

Basically Tusk will never re-render when given the same node twice, meaning the following will only render once.

let _ = require("lodash");

let MyDiv = _.memoize(function () {
    return (
        <div>Hello World</div>
    );
});

// creates and renders myDiv.
tusk.render(HTMLEntity, <MyDiv/>);

// noop.
tusk.render(HTMLEntity, <MyDiv/>);

// render something entirely different.
tusk.render(HTMLEntity, <MyOtherDiv/>);

// switch back - reuses existing "MyDiv" dom. (Extremely fast).
tusk.render(HTMLEntity, <MyDiv/>);

Stateful Nodes

Sometimes it makes sense to have full control over the DOM for certain types of components. In Tusk the framework won't fight you and instead provides an attribute for these types of nodes; "ignore". An ignored node will skip updates if the new node also has an "ignore" attribute.


function MyMap (props, children) {
    return (
        <div>
            Map: { props.title }
            The div below will only be rendered by tusk once, all updates are noops.
            <div ignore class="google-map" onMount={ confirgureGoogleMap }/>
        </div>
    );
}

Contributions

Please feel free to create a PR!