Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
pureact-reactive-web-components
Advanced tools
Readme
Pureact is a thiny library (less than 200 lines of code, 1.4kb minified and zipped) to use the reactive programming paradigm with web components. Like react, but with pure vanilla JavaScript.
The actual DOM manipulation is handled by morphdom a lightweight and super fast DOM diffing/patching library that works with actual DOM, no virtual DOM involved. Morphdom is at the core of marko.js the library used by Ebay.
Pureact has performance similar to React (same fps, slightly more memory usage), but offers several advantages over a virtual DOM library like React:
Caveats:
sanitizeString
function, but it must be used manually.Table of Contents
Write a custom element class in the usual way, then:
this.registerComponent()
, this is necessary to use inline event handlersextendComponent
function when you define the custom element:class MyCustomElement extends HTMLElement {
constructor() {
super();
this.registerComponent();
}
//Class implementation
}
window.customElements.define('my-custom-element', extendComponent(MyCustomElement));
Since extendComponent
is just a function that takes a class and returns a class it can be used with any HTML Element Class:
class MyCustomButton extends HTMLButtonElement {
constructor() {
super();
this.registerComponent();
}
//Class implementation
}
window.customElements.define('my-custom-button', extendComponent(MyCustomButton), {extends: button});
N.B. Morphdom must be included separately as a global function, e.g.:
<script src="https://cdn.jsdelivr.net/npm/morphdom@2.3.3/dist/morphdom.min.js"></script>
This is by design, in order to allow you to include a regular version, or a minified or a customized one and to let you inspect what's inside the dependency. Total control and transparency it the matra of this project.
extendComponent
takes as second argument an array thats specifies the attributes to watch for change: extendComponent(MyComponent: class, propsArray: string[])
It is used like this:
window.customElements.define('my-custom-element', extendComponent(MyCustomElelment, ['prop1', 'prop2']));
These attributes are automatically reflected to properties with the same name, and viceversa.
N.B. no conversion is made between hyphenated synthax typical of HTML attributes and camel case syntax typical of JS properties.
When reflecting attributes to properties, and the other way around, HTML attributes values (that are always strings) are automatically casted to the right type, while properties values are casted to strings.
N.B. boolean attributes are handled in the form attribute="true"
instead of regular HTML boolean attribute
with no value
When the propsArray
is passed extendComponent
automatically creates getters and setters for every property and automatically sets the observedAttributes
static method on the class to return the propsArray
.
To render a component a render
method must be defined, receiving the state as argument. Inside the method is possible to call the html
method that uses morphdom to update the DOM in a non destructive and very efficient way.
N.B. morphdom must be included separately as a global function, e.g.:
<script src="https://cdn.jsdelivr.net/npm/morphdom@2.3.3/dist/morphdom.min.js"></script>
the html
method must receive a string (tipically a template literal string) that represents the DOM to be rendered.
render() {
this.html(`<button onclick="${this.getHandlerRef(this.closePanel)}">
<span>${LeftArrow()}</span>
<span class="text">close</span>
</button>`);
}
N.B. Pay attention not to leave any spaces at the beginning of the string.
To have synthax highlight inside template literal string use a plugin for your IDE/Editor, e.g. ES6 String HTML for VS Code.
The HTML string can contain multiple children, it is not mandatory to have just one parent:
this.html(`<div>
<!-- content -->
</div>
<ul>
<!-- content -->
</ul>`);
}
To render multiple elements from an array use map
to convert the array into an array of strings, then join('')
to render the final HTML string:
this.html(`<ul>
${stores.map((store) => `<li>
<div>${store.name}</div>
</li>`}).join('')}
</ul>`);
To use a component's state just initialize a state
property in the class constructor:
constructor() {
super();
this.state = {
//state definition
}
}
Then, to update the state and rerender the component call the setState
method passing the (partial) state update.
Pureact can be used with any global state manager, like Redux. State change can be observed inside the connectedCallback
method. To avoid unnecessary rerendering is better to watch for a partial state change:
connectedCallback() {
subscribePartialState('stores', (state) => {
this.render(state.stores);
});
}
Good ol' inline event handlers, like this one:
<button onclick="clickHandler()"></button>
can be handy in writing declarative code, but they have a problem: they are executed when the event is triggered, so their scope is the global object and they loose any reference to this
.
The solution, as suggested in this article is to create a global registry of components with a reference to every component registered in the page, in order to be able to call a component method event form the global scope.
extendComponent
create such registry and provides two methods:
registerComponent()
to register a component in the registrygetHandlerRef(handlerName: string)
to get e reference to a component's method to pass as an inline event handlerFirst register the component
constructor() {
super();
this.registerComponent();
}
then define an event handler as a component's method
handleToggle() {
//Handle toggle search
}
finnally use this.getHandlerRef
to get a reference to the method to pass as an inline event handler
render() {
this.html(`<form class="searchWrapper">
<button onclick="${this.getHandlerRef(this.handleToggle)}">toggle</button>
`);
}
you can pass any number of arguments to events handler:
<ul>
${storeTypes.map((storeType) => {
return `<li>
<button class="storeTypeBtn"
onclick="${this.getHandlerRef(this.handleFilterClick, storeType)}">
<span class="icon">x</span>
<span class="text">${storeType.name}</span>
</button>
</li>
`
}).join('')}
</ul>
the handler will receive those arguments, plus the event
object as first parameter:
handleFilterClick(event, storeType) {
dispatch(toggleStoreTypeAction(storeType.id));
}
Besides the standard custom elements lifecycle methods, Pureact adds:
propertyChangedCallback
is the same as attributeChangedCallback
but with parameters converted from stringscomponentWillUpdate
is called before rendering the componentcomponentDidUpdate
is called after the component has been renderedFunctional stateless components are just regular functions that take props and return an HTML string:
export default (handleClick) => `<label>
<input id="toggleAdvancedSearch" type="checkbox" onclick="${handleClick}">
<span>Advanced Search</span>
</label>`;
To render a child component:
SearchIcon
in the example below)renderChildComponent
method (search-suggestion
in the example below). This is necessary bacause the parent component rerendering will otherwise reinitialize the child component and destroy its internal state: render(searchTerm) {
this.html(`<div>
<label for="searchInput" >Search a store</label>
<div>
${SearchIcon()}
<div>
<input id="searchInput" type="text" placeholder="Search..." oninput="${`document._componentRegistry['${this._id}'].handleInput(event)`}" value="${searchTerm}"/>
${this.renderChildComponent('search-suggestions')}
</div>
</div>
</div>`);
}
To render CSS via JS just include a <style>
node in the HTML string passed to the html
method:
this.html(`
<style>
filter-panel {
padding: var(--padding);
}
.filter-panel_toggleBtn {
display: inline-flex;
align-items: center;
}
.filter-panel_toggleBtn_text {
margin-right: 0.5rem;
}
</style>
<div class="filter-bar_header">
<!--...-->
</div>`);
}
To encapsulate the CSS in the component generate a unique class name with randomizeCssClass
:
render(stores) {
const storeNameCssClass = this.randomizeCssClass('storeName');
this.html(`<style>
.${storeNameCssClass} {
margin-bottom: 0.5em;
}
</style>
<ul>
${stores
.map((store, i) => /*html*/`
<li class="${storeNameCssClass}">
${store.name}
</li>`).join('')}
</ul>
`;
}
Pureact provides getAnimationClass(currentState: boolean, prevState: boolean, classList: string[]): string
to choose the class to apply the right CSS animation, based on current and previous state
classList is an array with 4 values in this order:
const animationClass = getAnimationClass(isActive, wasActive, ['invisible', 'fade-in', 'visible', 'fade-out']);
return `<li>
<button class="storeTypeBtn"
<span class="icon ${animationClass}">x</span>
<span class="text">${storeType.name}</span>
</button>
</li>`;
FAQs
A thiny library to use the reactive programming paradigm with web components
The npm package pureact-reactive-web-components receives a total of 0 weekly downloads. As such, pureact-reactive-web-components popularity was classified as not popular.
We found that pureact-reactive-web-components demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.