Stencil is not a JS framework. It is a compiler that produces a reusable web component that can be embedded anywhere else.
This is a step by step guide to consume a non-trivial stencil component in a React app.
The starter react app was created with create-react-app.
Creating your first stencil component is very easy and it is well documented here.
This example will consume two components:
Add the component to the app dependencies in package.json
// package.json
"dependencies": {
...
"@openchemistry/molecule-vtkjs": "^0.3.2",
"split-me": "^1.1.4"
}
Import the component in the index.js
of the app:
import { defineCustomElements as defineMolecule } from '@openchemistry/molecule-vtkjs/dist/loader';
import { defineCustomElements as defineSplitMe } from 'split-me/dist/loader';
defineMolecule(window);
defineSplitMe(window);
It is now possible to use the tag provided by the stencil component in the render
function of any react component.
render() {
return (
<split-me n="2">
<oc-molecule-vtkjs slot="0"></oc-molecule-vtkjs>
<oc-molecule-vtkjs slot="1"></oc-molecule-vtkjs>
</split-me>
)
}
oc-molecule-vtkjs
has a property named cjson
that expects an object (or a JSON.stringified object).
Strings and numbers can be passed directly as attributes to a stencil component.
One way to pass a complex object to a component could be to JSON.stringify()
the object and then JSON.parse()
it inside the component. But this round trip can be expensive, and it would be a good idea to pass the object directly as a prop.
React doesn't provide a convenient way to distinguish between attribute and prop, so a little work is needed to achieve this.
It just boils down to saving a reference to the element of the stencil component, and then set the property directly in the javascript code.
To make this operation easier, it can be convenient to create a reusable utility function wc
.
export function wc(customEvents = {}, props = {}) {
let storedEl;
return function (el) {
for (let name in customEvents) {
let value = customEvents[name] ;
// If we have an element then add event listeners
// otherwise remove the event listener
const action = (el) ? el.addEventListener : storedEl.removeEventListener;
if (typeof value === 'function') {
action(name, value);
return;
}
}
// If we have an element then set props
if (el) {
for (let name in props) {
let value = props[name] ;
el[name] = value;
}
}
storedEl = el;
};
}
And then use it in the jsx
to bind events and properties to the webcomponent this way:
import React, { Component } from 'react';
import { wc } from './utils/webcomponent';
class SomeComponent extends Component {
render() {
return (
<div style={{width: "100%", height: "20rem", position: "relative"}}>
<oc-molecule-vtkjs
ref={wc(
// Events
{},
// Props
{
cjson: molecule
}
)}
/>
</div>
);
}
}
export default SomeComponent;