microsite
Version:
<br /> <br />
103 lines (102 loc) • 3.37 kB
JavaScript
// Adapted from https://github.com/pmndrs/valtio
import { useEffect, useMemo, useState } from "preact/hooks";
const LISTENERS = Symbol();
const SNAPSHOT = Symbol();
const isObject = (x) => typeof x === "object" && x !== null;
const createProxy = (initialObject = {}) => {
let version = 0;
const listeners = new Set();
const incrementVersion = () => {
version = version + 1;
listeners.forEach((listener) => listener());
};
const proxy = new Proxy(Object.create(initialObject.constructor.prototype), {
get(target, prop) {
if (prop === LISTENERS) {
return listeners;
}
if (prop === SNAPSHOT) {
const snapshot = Object.create(target.constructor.prototype);
Reflect.ownKeys(target).forEach((key) => {
const value = target[key];
if (isObject(value)) {
snapshot[key] = value[SNAPSHOT];
}
else {
snapshot[key] = value;
}
});
return snapshot;
}
return target[prop];
},
deleteProperty(target, prop) {
const value = target[prop];
if (isObject(value)) {
value[LISTENERS].delete(incrementVersion);
}
delete target[prop];
incrementVersion();
return true;
},
set(target, prop, value) {
if (isObject(value)) {
if (value[LISTENERS]) {
target[prop] = value;
}
else {
target[prop] = createProxy(value);
}
target[prop][LISTENERS].add(incrementVersion);
}
else {
target[prop] = value;
}
incrementVersion();
return true;
},
});
Reflect.ownKeys(initialObject).forEach((key) => {
proxy[key] = initialObject[key];
});
return proxy;
};
const subscribe = (proxy, callback) => {
proxy[LISTENERS].add(callback);
return () => {
proxy[LISTENERS].delete(callback);
};
};
const getSnapshot = (proxy) => proxy[SNAPSHOT];
export const createGlobalState = createProxy;
export const useGlobalState = (source = {}) => {
const subscription = useMemo(() => ({
getCurrentValue: () => getSnapshot(source),
subscribe: (callback) => subscribe(source, callback),
}), [source]);
const [state, setState] = useState(() => ({
value: subscription.getCurrentValue(),
}));
let valueToReturn = state.value;
useEffect(() => {
let didUnsubscribe = false;
const checkForUpdates = () => {
if (didUnsubscribe)
return;
const value = subscription.getCurrentValue();
setState((prevState) => {
if (prevState.value === value) {
return prevState;
}
return { value };
});
};
let unsubscribe = subscription.subscribe(checkForUpdates);
checkForUpdates();
return () => {
didUnsubscribe = true;
unsubscribe();
};
}, [subscription]);
return valueToReturn;
};