I was just chatting with Dave and he told me about Haunted. It’s hooks, but for native web components! Pretty cool. I think the existence of stuff like this makes using web components more and more palatable — particularly in that totally-native no-build-step-needed-at-all kinda way.
I get that there are all sorts of issues with web components, but the things that typically turn me away from them are a lack of nice templating and rerendering and no state management.
But we can knock those two out right quick these days…
First, making a component like <my-app>
is perfectly comfortable:
import { html } from "https://unpkg.com/lit-html/lit-html.js";
import { component } from "https://unpkg.com/haunted/haunted.js";
function App() {
return html`
<div class="module">
Hello, World!
</div>
`;
}
customElements.define("my-app", component(App));
Then we could add some state with hooks:
import { html } from "https://unpkg.com/lit-html/lit-html.js";
import { component, useState} from "https://unpkg.com/haunted/haunted.js";
function App() {
const [name, setName] = useState("Chris");
return html`
<div class="module">
Hello, ${name}!
</div>
`;
}
customElements.define("my-app", component(App));
The CodePen Challenge this week is using the Star Wars API, so let’s make a fetch request and use that to fill state. That’s a great use case for useEffect.
import { html } from "https://unpkg.com/lit-html/lit-html.js";
import { component, useState, useEffect } from "https://unpkg.com/haunted/haunted.js";
function App() {
const [planets, setPlanets] = useState([]);
useEffect(() => {
fetch('https://swapi.co/api/planets/?page=2')
.then(response => {
return response.json();
})
.then(data => {
let planets = data.results;
// remove ones with no diameters
planets = planets.filter(planet => planet.diameter !== "0");
setPlanets(planets);
});
}, []);
return html`
<style>
/* Shadow DOM styles */
</style>
<div class="all-planets">
${planets.map(planet => html`
<div class="planet" style="--dia: ${planet.diameter}px">
<span class="planet-name">
${planet.name}
</span>
</div>
`)}
</div>
`;
}
customElements.define("my-app", component(App));
That’s a proper little web component!
See the Pen
Star Wars API with Haunted.js by Chris Coyier (@chriscoyier)
on CodePen.
I love the idea of something like this. However, I don’t know how to reconcile the fact that adding a library makes me feel like the web components I’m using are no longer native. How do you do it?
You can write your own version pretty easily. Example: https://codesandbox.io/s/wc-simple-hooks-b3sh5
I’m using lit-html, but you can remove that too if you want…
Not looking to write everything myself. Just wondering how to reconcile the use of native along with non-native libraries. I just have my own personal conflict with it. Wondering how others reconcile that
I know what you mean. Now your buy-in is just somewhere else. But they still feel fairly native to me:
<my-thing>
in implementation in HTMLAwesome to see react-like code without all the synthetic stuff and with better interop. Thanks!
Too bad neither HyperHTMLElement nor lighterhtml are mentioned, ’cause haunted works with various other libraries too.
There’s also a discussion about dropping lit-html from its core, so that Stencil, among other frameworks, might benefit as well.