Pureact: reactive web components
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:
- minimal footprint (1.4kb + 2.6kb for morphodom, minified and zipped)
- works with standard JavaScript features, no fancy syntax to learn, no update hell, no framework obsolescence
- just one tiny dependency, this means the maximum of transparency and no security threaths hidden in the dependency tree
- working with actual DOM means a super easy integration with other libraries that perform DOM manipulation, like jQuery plugins, for instance
Caveats:
- No automatic input sanitification. The library provides a
sanitizeString
function, but it must be used manually.
Table of Contents
- Getting Started
- Watch for props change and reflect attributes to properties
- Render a component
- State management
- Inline events handlers
- Lifecycle Methods
- Child Component
- CSS in JS
- Animations
- Demo
- Demo Code
Getting started
Write a custom element class in the usual way, then:
- in the constructor register the component calling
this.registerComponent()
, this is necessary to use inline event handlers - wrap the class with the
extendComponent
function when you define the custom element:
{ super; this; } //Class implementation windowcustomElements;
Since extendComponent
is just a function that takes a class and returns a class it can be used with any HTML Element Class:
{ super; this; } //Class implementationwindowcustomElements;
N.B. Morphdom must be included separately as a global function, e.g.:
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.
Watch for props change and reflect attributes to properties
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:
windowcustomElements;
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
.
Render a component
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.:
the html
method must receive a string (tipically a template literal string) that represents the DOM to be rendered.
{ this;}
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; }
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;
State management
To use a component's state just initialize a state
property in the class constructor:
{ super; thisstate = //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:
{ ;}
Inline events handlers
Good ol' inline event handlers, like this one:
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 handler
First register the component
{ super; this; }
then define an event handler as a component's method
{ //Handle toggle search }
finnally use this.getHandlerRef
to get a reference to the method to pass as an inline event handler
{ this; }
you can pass any number of arguments to events handler:
<ul> $storeTypes</ul>
the handler will receive those arguments, plus the event
object as first parameter:
{ ;}
Lifecycle Methods
Besides the standard custom elements lifecycle methods, Pureact adds:
propertyChangedCallback
is the same asattributeChangedCallback
but with parameters converted from stringscomponentWillUpdate
is called before rendering the componentcomponentDidUpdate
is called after the component has been rendered
Functional Stateless Component
Functional stateless components are just regular functions that take props and return an HTML string:
`<label> <input id="toggleAdvancedSearch" type="checkbox" onclick=""> <span>Advanced Search</span> </label>`;
Child Component
To render a child component:
- just drop it in, if it is a functional stateless component (
SearchIcon
in the example below) - if it is a custom element, wrap the custom element tag it in the
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:
{ this; }
CSS in JS
To render CSS via JS just include a <style>
node in the HTML string passed to the html
method:
this; }
To encapsulate the CSS in the component generate a unique class name with randomizeCssClass
:
{ const storeNameCssClass = this; this
Animations
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:
- base state
- progress animation
- animation ended state
- reverse animation
const animationClass = ;return `<li> <button class="storeTypeBtn" <span class="icon ">x</span> <span class="text"></span> </button> </li>`;