Mobx heavily uses "magic" (i.e. hides away most of the reactions/observations by default and 'figures them out').
Not everyone is a fan of this but it has proven to be fairly popular, so i had a go at doing similar with atom.
Here's what i have:
Usage
export class MyElement extends AtomLitElement {
render() {
return html`
<button @click="${() => setCount(old => old - 1)}">-</button>
<span>${count.getState()}</span>
<button @click="${() => setCount(old => old + 1)}">+</button>
`;
}
}
Implementation
import {LitElement, html, PropertyValues} from 'lit-element';
// NOTE: atoms isn't currently exported
import {atom, atoms} from "@klaxon/atom";
const [count, setCount] = atom({
key: 'count',
default: 0
});
// Truly excellent name
class AtomicReactor {
public callback: () => void;
protected _accessed: Set<string> = new Set();
public constructor(callback: () => void) {
this.callback = callback;
}
public track(): () => void {
const originalGet = atoms.get;
// NOTE: overwriting a method like this always feels like hackery
// but the only other way i suppose is to have atom itself have a concept
// of notifying of accesses (e.g. an event fired when one is accessed, but more
// performant than that).
atoms.get = (key: string) => {
this._accessed.add(key);
return originalGet.call(atoms, key);
};
return () => {
atoms.get = originalGet;
for (const [key, atom] of atoms.entries()) {
if (this._accessed.has(key)) {
// NOTE: needs to be removed at some point so we don't fill
// memory with event listeners over time
atom.addEventListener(key, () => {
this.callback();
});
}
}
};
}
}
const reactor = Symbol();
class AtomLitElement extends LitElement {
protected [reactor]?: AtomicReactor;
public connectedCallback(): void {
super.connectedCallback();
this[reactor] = new AtomicReactor(() => {
this.requestUpdate();
});
}
public disconnectedCallback(): void {
super.disconnectedCallback();
// NOTE: probably needs a this[reactor].dispose() which
// removes all those previously mentioned listeners
this[reactor] = undefined;
}
protected update(change: PropertyValues): void {
const reactorInstance = this[reactor];
if (reactorInstance) {
const disposer = reactorInstance.track();
super.update(change);
disposer();
} else {
super.update(change);
}
}
}
Mobx heavily uses "magic" (i.e. hides away most of the reactions/observations by default and 'figures them out').
Not everyone is a fan of this but it has proven to be fairly popular, so i had a go at doing similar with atom.
Here's what i have:
Usage
Implementation