v0.0.4-alpha
Simple store for Deno Fresh, to pass state between islands
Repository
Current version released
2 years ago
Dependencies
esm.sh
Fresh Store
A minimal store for Fresh, to allow communication between islands. It attach stores to the window
component. It uses “pointers” or “keys” to associate stores. A pointer can either be provided, or auto-generated.
Usage
Creating a store.
const ptr = useStore(
"Initial Value",
(newState) => console.log(`New value: ${newState}`),
);
console.log(window.stores.get<string>(ptr).state);
window.stores.get<string>(ptr).set("Modified Value");
Output:
Initial Value
New value: Modified Value
Creating a store and providing a pointer.
const ptr = crypto.randomUUID();
useStore(
"Initial Value",
(newState) => console.log(`New value: ${newState}`),
ptr,
);
console.log(window.stores.get<string>(ptr).state);
window.stores.get<string>(ptr).set("Modified Value");
Output:
Initial Value
New value: Modified Value
Creating a new Observer
const storePtr = useStore("New Store", (_) => null);
class ConcreteObserver implements Observer<T> {
public update(subject: Store<T>): void {
console.log("The store was updated, new state: ", subject.state);
}
}
window.stores.get(storePtr).attach(new ConcreteObserver());
Example usage in components
// ./islands/componentA.tsx
/** @jsx h */
import { h } from "preact";
interface CompAProps {
storePtr: string;
}
export default function ComponentA(props: CompAProps) {
useStore<number>(0, () => null, props.storePtr);
const increment = () =>
window.stores
.get<number>(props.storePtr)
.set((state) => state + 1);
const decrement = () =>
window.stores
.get<number>(props.storePtr)
.set((state) => state - 1);
return (
<div>
<button onClick={decrement}>-1</button>
<button onClick={increment}>+1</button>
</div>
);
}
// ./islands/componentB.tsx
/** @jsx h */
import { h } from "preact";
import { useEffect, useState } from "preact/hooks";
// Change depending on your import_map
import type { Observer, Store } from "@store";
import { StoreStack } from "@store";
interface CompBProps {
storePtr: string;
}
export default function ComponentB(props: CompBProps) {
const [counter, setCounter] = useState(0);
useEffect(() => {
class CounterObserver implements Observer<number> {
public update(subject: Store<number>) {
setCounter(subject.state);
}
}
const observer = new CounterObserver();
// Makes sure `window.stores` is defined.
StoreStack.configure();
// Creates the store if it does not yet exist.
window.stores.upsert<number>(counter, props.storePtr, observer);
// Sets the counter value to the value in the store.
setCounter(window.stores.get<number>(props.storePtr)!.state);
// Detaches the observer on cleanup.
return () => window.stores.get(props.storePtr).detach(observer);
}, [counter]);
return <p>Counter: {counter}</p>;
}
// ./routes/index.tsx
/** @jsx h */
import { h } from "preact";
import ComponentA from "@islands/componentA.tsx";
import ComponentB from "@islands/componentB.tsx";
export default function Index() {
const storePtr = crypto.randomUUID();
return (
<div>
<ComponentA storePtr={storePtr} />
<ComponentB storePtr={storePtr} />
</div>
);
}