Provides a way to use function, class, and stateful components with diffHTML. Inspired by React and other component based frameworks.
Stable Version: 1.0.0-beta.30
The exported class Component
is designed to be used as either a vanilla JS
class component, or registered as a Web Component.
Refer to the website https://diffhtml.org/components.html for documentation.
npm install --save diffhtml-components
Before you can use this module, you will need to have diffHTML loaded first.
This component simply provides the Component
class, and respective
middleware, which help you create Virtual Trees to structure your code.
You can create components as easy as:
import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';
class MyComponent extends Component {
render() {
return html`
<h1>Rendering a Web Component inside a React-Like Component:</h1>
<web-component />
`;
//return (
// <span>Even Supports JSX (use Babel transform)!</span>
//);
}
}
// Render as a class.
innerHTML(document.body, html`<${MyComponent} />`);
// If you are in a DOM environment that supports Web Components, then you can
// register your component and it will automatically work.
customElements.define('my-component', MyComponent);
// Now you can render like any other kind of HTML.
innerHTML(document.body, '<my-component />');
This static definition on either a function or class component provides a specification for incoming props. It borrows from the React concept. These are very important for Web Components as they set up the observable properties. That means you can simply update a property directly on the DOM Node and it will automatically re-render.
Example of default props with a class component:
import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';
class MyComponent extends Component {
render() {
const { message } = this.props;
return html`${message}`;
}
static defaultProps = {
message: 'default',
}
}
innerHTML(document.body, html`
<${MyComponent} message="setting props via class" />
`);
// Defining your component as a web component is completely optional and easy,
// just use the standard `customElements.define` function and provide a tag
// name. Now <my-component prop=value>childNodes[]</my-component> should work
// as expected.
customElements.define('my-component', MyComponent);
innerHTML(document.body, html`
<my-component message="setting props via web component" />
`);
document.body.firstElementChild.message = 'Dynamic!';
// my-component has now automatically re-rendered to display this new state
diffHTML Components are stateful and follow the React model of setState
. By
default your components are given an empty state
object that you can use to
store any kind of local state for your component. While you can edit this
object directly, this will not trigger any kind of component update. To set
the new state and trigger a re-render, you will need to use the setState
method.
For example:
import { html } from 'diffhtml';
import { Component } from 'diffhtml-components';
class SimpleCounter extends Component {
render() {
const { className, label } = this.props;
return html`
<div class=${className}>${label}</div>
`;
}
componentDidMount() {
// Increment the `tick` state every second.
this.interval = setInterval(() => this.setState({
tick: ++this.state.tick,
}), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
}
The setState
call receives the incoming object, which is merged into the
existing this.state
and then into a brand new object, which effectively
causes your state
property to be "immutable".
If you were to call setState
in a tight loop, you would notice that the first
call would trigger a re-render, but would not render again until the tight
loop completes. This is a way to throttle while also allowing setState
to be
reliable for synchronous operations.
If you do not like the behavior and wish to have more control over setting
state and rendering changes, look into forceUpdate
below.
The forceUpdate()
method is implemented and will trigger a no-questions-asked
-render of your component. This is a synchronous operation, but if diffHTML
has a paused transaction, this will wait until the existing transaction has
completed before modifying your component.
This is especially useful if you want to manage state outside of setState
and
want your components to be reactive.
The following examples show what real-world usage of these components may look like.
Using a function component is a good way to have lightweight components without
the overhead of a class. You map the incoming attributes as arguments and the
return value is a VTree created by html
or createTree
.
import { html, innerHTML } from 'diffhtml';
import 'diffhtml-components';
function SimpleClock({ now }) {
return html`
<strong>The current unix timestamp is:</strong> ${now}
`;
}
innerHTML(document.body, html`<${SimpleClock} now=${Date.now()} />`);
Useful when you need minimal React features for new projects. This is not
necessarily a good package for React Compatibility inter-op, although it can
work to load some components. If you need full parity with React, look to the
diffhtml-react-compat
.
import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';
class SimpleClock extends Component {
render() {
const { now } = this.state;
return html`
<strong>The current unix timestamp is:</strong> ${now}
`;
}
constructor(props) {
super(props);
this.state = { now: Date.now() };
// Update very fast.
setInterval(() => this.setState({ now: Date.now() }), 10);
}
}
// Render to the `<body />` element.
innerHTML(document.body, html`<${SimpleClock} />`);
All diffHTML Components extend HTMLElement and can be registered with the V1
Custom Elements registry. It is optional to utilize them as Web Components. To
do so, use the customElements.define
function.
- Chrome: Stable
- Firefox: Stable
- Safari: Stable
- Edge: Stable
import { html, innerHTML } from 'diffhtml';
import { Component } from 'diffhtml-components';
class SimpleClock extends Component {
render() {
const { now } = this.state;
return html`
<strong>The current unix timestamp is:</strong> ${now}
`;
}
constructor(props) {
super(props);
this.state = { now: Date.now() };
// Update very fast.
setInterval(() => this.setState({ now: Date.now() }), 10);
}
}
// Required to auto-fill the `observedAttributes` function.
SimpleClock.defaultProps = {
now: Date.now(),
};
// Register into the browser's element registry.
customElements.define('simple-clock', SimpleClock);
// Render to the `<body />` element.
innerHTML(document.body, html`<simple-clock />`);