React's Hooks API but for standard web components and [hyperHTML](https://codepen.io/WebReflection/pen/pxXrdy?editors=0010) or [lit-html](https://polymer.github.io/lit-html/).
npm install @matthewp/hauntedReact's Hooks API but for standard web components and hyperHTML or lit-html.
``html
`
A starter app is available on codesandbox and also can be cloned from this repo. This app gives you the basics of how to use Haunted and build components.
Currently __Haunted__ is available as the @matthewp/haunted package. In the future I hope to get the non-scoped name.
`shell`
npm install @matthewp/haunted
Haunted comes in a few builds. Pick one based on your chosen environment:
* __index.js__ is available for bundlers such as Webpack and Rollup. Use with: import { useState } from '@matthewp/haunted';;import { useState } from '../node_modules/@matthewp/haunted/web.js';
* __web.js__ is avaible for use with the web's native module support. Use with: .import { useState } from 'https://unpkg.com/@matthewp/haunted/haunted.js';
* __haunted.js__ is available via the CDN unpkg. This is great for small apps or for local development without having to install anything. Use with
Haunted is all about writing plain functions that can contain their own state. The follow docs is divided between creating components (the functions) and using hooks the state.
Components are functions that contain state and return HTML via lit-html or hyperHTML. Through the component() and virtual() they become connected to a lifecycle that keeps the HTML up-to-date when state changes.
Using Haunted you can create custom elements or virtual components (components that contain state but have no element tag).
#### Custom elements
The easiest way to create components is by importing component and creating a custom element like so:
`js
import { component } from '@matthewp/haunted';
import { html } from 'lit-html';
const App = component(({ name }) => {
return htmlHello ${name}!;
});
customElements.define('my-app', App);
`
You can now use this anywhere you use HTML (directly in a .html file, in JSX, in lit-html templates, whereever).
Here's an example of rendering with lit-html the above app:
`js
import { render, html } from 'lit-html';
render(html
, document.body);`
#### Virtual components
Haunted also has the concept of virtual components. These are components that are not defined as a tag. Rather they are functions that can be called from within another template. They have their own state and will rerender when that state changes, without causing any parent components to rerender.
The following is an example of using virtual components:
`js
import { useState, virtual, component } from '@matthewp/haunted';
import { html, render } from 'lit-html';
const Counter = virtual(() => {
const [count, setCount] = useState(0);
return html
@click=${() => setCount(count + 1)}>${count}
;
});
const App = component(() => {
return html
My app
${Counter()}
;
});
customElements.define('my-app', App);
`
Notice that we have Counter, a virtual component, and App, a custom element. You can use virtual components within custom elements and custom elements within virtual components.
The only difference is that custom elements are used by using their tag name and virtual components are called as functions.
If you wanted you could create an entire app of virtual components.
Haunted supports the same API as React Hooks. The hope is that by doing so you can reuse hooks available on npm simply by aliasing package names in your bundler's config.
Currently Haunted supports the following hooks:
#### useState
Create a tuple of state and a function to change that state.
`js`
const [count, setCount] = useState(0);
#### useEffect
Useful for side-effects that run after the render has been commited.
`html
`
##### Memoization
Like useMemo, useEffect can take a second argument that are values that are memoized. The effect will only run when these values change.
`js
function App() {
let [name, setName] = useState('Dracula');
useEffect(() => {
// This only occurs when name changes.
document.title = Hello ${name};
}, [name]);
return html...;`
}
##### Cleaning up side-effects
Since effects are used for side-effectual things and might run many times in the lifecycle of a component, useEffect supports returning a teardown function.
An example of when you might use this is if you are setting up an event listener.
`js
function App() {
let [name, setName] = useState('Wolf Man');
useEffect(() => {
function updateNameFromWorker(ev) {
setName(ev.data);
}
worker.addEventListener('message', updateNameFromWorker);
return () => {
worker.removeEventListener('message', updateNameFromWorker);
}
});
return html...;`
}
#### useReducer
Create state that updates after being ran through a reducer function.
`html
`
#### useMemo
Create a memoized state value. Only reruns the function when dependent values have changed.
`html
`
#### useContext
Grabs context value from the closest provider up in the tree and updates component when value of a provider changes.
Limited only to "real" components for now
`html
``
BSD-2-Clause