React's Hooks API implemented for web components 👻
BSD-2-CLAUSE License
Bot releases are visible (Hide)
Published by github-actions[bot] over 2 years ago
Published by matthewp almost 5 years ago
Adds the initializer argument to useReducer, #160.
Published by matthewp almost 5 years ago
Fail gracefully when useMemo
is called without deps. https://github.com/matthewp/haunted/pull/153
Published by matthewp about 5 years ago
Fixes a bug that prevented the use of the title
attribute.
Published by matthewp about 5 years ago
This is a huge release for Haunted, one of the biggest since the initial release. Before getting into features I want to give some appreciate to @jdin, @Gladear, and @chase-moskal who all made tremendous contributions to this release. That cannot go overstated, I did only a small part of this release, the Haunted community is what keeps this project alive. ❤️ 🎃 . Now that the sappy stuff is complete, on to the features!
At its core Haunted is a container for state and lifecycle callbacks that is derived from hooks like useState
, useMemo
, even useEffect
. With the component()
API you get more than just that, you also get a scheduler that handles rendering and committing the result.
A lot of people have wanted to use hooks outside of the context of Haunted components. One example is using hooks inside of a LitElement component. The new State
API enables this, by exposing the low-level part of Haunted which is its state container.
Here's an example of how State
can be used to mixin with other base element classes:
import { LitElement } from 'lit-element';
import { State } from 'haunted';
export default class LitHauntedElement extends LitElement {
constructor() {
super();
this.hauntedState = new State(() => this.requestUpdate(), this);
}
update(changedProperties) {
this.hauntedState.run(() => super.update(changedProperties));
this.hauntedState.runEffects();
}
}
When you would then use like:
import LitHauntedElement from './lit-haunted-element.js';
import { useState } from 'haunted';
import { html } from 'lit-element';
class MyApp extends LitHauntedElement {
render() {
const [count, setCount] = useState(0);
return html`
<div>Count: ${count}</div>
<button @click=${() => setCount(count + 1)}>
Increment
</button>
`;
}
}
See this gist for other example integrations.
@jdin has built a new LitElement integration which can be installed, haunted-lit-element. 🎉
This is one of the more obscure hooks, but useLayoutEffect
is useful when you want to do something right after the DOM is rendered. An example would be setting up adoptedStyleSheets
like so:
import { useLayoutEffect, component } from 'haunted';
import { css, html } from 'lit-element';
function useAdoptedStylesSheets(el, ...sheets) {
useLayoutEffect(() => {
el.adoptedStyleSheets = [
...el.adoptedStyleSheets,
...sheets
];
});
}
const styles = css`
h1 { color: blur; }
`;
function MyApp() {
useAdoptedStyleSheets(this, styles);
return html`
<h1>Hello world!</h1>
`;
}
customElements.define('my-app', component(MyApp));
Haunted is now written in TypeScript, so all of the problems that TypeScript users have had in the past with imperfect type definitions should not be a problem going forward. Huge thanks to @Gladear who implemented the conversion (as well as useLayoutEffect
)! 🎉
Published by matthewp about 5 years ago
Minor fixes in typings from #131
Published by matthewp about 5 years ago
This is a patch release, which fixes #127 and ensures that Haunted components are instancesof HTMLElement.
Published by matthewp about 5 years ago
🦇 🎃 This is an initial beta release of the new State
API. The State API is a low-level way to create instances of hooks state. It provides methods for running hooks code and a callback for when any hook state has changed. The intended use-case is integration with base classes such as SkateJS and LitElement.
Since the API is lower-level, we feel it should be first released as a beta so developers interested in creating integrations with base classes can try it out and see if there are any holes to be filled. When those developers as satisfied it will come in a future minor release, likely 4.6.0.
Here's a small example of how the State
API works at its lowest level:
import { State, useState } from 'haunted';
let state = new State(() => {
update();
});
function update() {
state.run(() => {
const [count, setCount] = useState(0);
console.log('count is', count);
setTimeout(() => setCount(count + 1), 3000);
});
}
update();
More example integrations can be found in this gist. Also see the documentation in the readme here.
Published by matthewp over 5 years ago
This is a small fix, ensuring that everything is exported from core.js so that the hooks can be imported from this entry point as well.
Published by matthewp over 5 years ago
👻 Haunted 4.5.0 is out 🎉. This is a pretty big release as it introduces some nice new features:
the new haunted/core
entry point is ideal for those who are using a templating library other than lit-html, for example lighterhtml. This has been one of the most requested features for Haunted for a long time. Although Haunted has always worked with hyperHTML, lighterhtml, and others, it always included lit-html even if it was unused.
I didn't like the idea of not including support for a templating library out-of-the-box and forcing configuration on everything, so Haunted still works the easy way with lit-html. The new haunted/core
is for people who want a little more control. The haunted
export is a function that wires together a render function and produces the component
function, createContext
, and a few other things. Here's a simple example:
import haunted, { useState } from 'haunted/core';
import { html, render } from 'lighterhtml';
const { component } = haunted({
render(what, where) {
render(where, () => what);
}
});
const App = component(() => {
const [count, setCount] = useState(0);
return html`Using lighterhtml! Count: ${count}`;
});
Here a codepen using haunted/core with lighterhtml.
The useRef
hook has been added. It's a simple way to create a mutable value. An example usage is like so:
function App() {
const myRef = useRef(0);
return html`
${myRef.current}
`;
}
We're revamped the docs that go over all of the ways that Haunted can be imported. We've also simplified things so that there's the haunted.js
bundle that is useful from a CDN for things like demo pages, and the haunted
and haunted/core
entry points for use with bundlers / other tooling. See the new docs for more.
Published by matthewp over 5 years ago
Now supports the delegatesFocus
option in Shadow DOM via the new shadowRootInit
option.
function App() {
return html`delegates focus`;
}
customElements.define('delegates-focus', app,
HTMLElement, {shadowRootInit: {delegatesFocus: true}));
Published by matthewp over 5 years ago
This fixes useEffect
typings.
Published by matthewp over 5 years ago
This is a minor release adding TypeScript typings as an officially supported thing in Haunted. 🎉
Published by matthewp over 5 years ago
This fixes an issue with useEffect
teardown functions not being called in virtual components.
Published by matthewp over 5 years ago
This patch release fixes the web.js build.
Published by matthewp over 5 years ago
This is a minor release adding 2 new features.
First, it's now possibly to disable the use of Shadow DOM:
component(() => html`...`, HTMLElement, {useShadowDOM: false}))
Secondly, dash attributes are now correctly converted to camelCased properties:
function Drawer({ isOpen }) {
return html` ... `
}
Drawer.observedAttributes = ['is-open'];
<my-drawer is-open></my-drawer>
Big thanks to @jpray for developing these!