UNPKG

3.37 kBJavaScriptView Raw
1// Adapted from https://github.com/pmndrs/valtio
2import { useEffect, useMemo, useState } from "preact/hooks";
3const LISTENERS = Symbol();
4const SNAPSHOT = Symbol();
5const isObject = (x) => typeof x === "object" && x !== null;
6const createProxy = (initialObject = {}) => {
7 let version = 0;
8 const listeners = new Set();
9 const incrementVersion = () => {
10 version = version + 1;
11 listeners.forEach((listener) => listener());
12 };
13 const proxy = new Proxy(Object.create(initialObject.constructor.prototype), {
14 get(target, prop) {
15 if (prop === LISTENERS) {
16 return listeners;
17 }
18 if (prop === SNAPSHOT) {
19 const snapshot = Object.create(target.constructor.prototype);
20 Reflect.ownKeys(target).forEach((key) => {
21 const value = target[key];
22 if (isObject(value)) {
23 snapshot[key] = value[SNAPSHOT];
24 }
25 else {
26 snapshot[key] = value;
27 }
28 });
29 return snapshot;
30 }
31 return target[prop];
32 },
33 deleteProperty(target, prop) {
34 const value = target[prop];
35 if (isObject(value)) {
36 value[LISTENERS].delete(incrementVersion);
37 }
38 delete target[prop];
39 incrementVersion();
40 return true;
41 },
42 set(target, prop, value) {
43 if (isObject(value)) {
44 if (value[LISTENERS]) {
45 target[prop] = value;
46 }
47 else {
48 target[prop] = createProxy(value);
49 }
50 target[prop][LISTENERS].add(incrementVersion);
51 }
52 else {
53 target[prop] = value;
54 }
55 incrementVersion();
56 return true;
57 },
58 });
59 Reflect.ownKeys(initialObject).forEach((key) => {
60 proxy[key] = initialObject[key];
61 });
62 return proxy;
63};
64const subscribe = (proxy, callback) => {
65 proxy[LISTENERS].add(callback);
66 return () => {
67 proxy[LISTENERS].delete(callback);
68 };
69};
70const getSnapshot = (proxy) => proxy[SNAPSHOT];
71export const createGlobalState = createProxy;
72export const useGlobalState = (source = {}) => {
73 const subscription = useMemo(() => ({
74 getCurrentValue: () => getSnapshot(source),
75 subscribe: (callback) => subscribe(source, callback),
76 }), [source]);
77 const [state, setState] = useState(() => ({
78 value: subscription.getCurrentValue(),
79 }));
80 let valueToReturn = state.value;
81 useEffect(() => {
82 let didUnsubscribe = false;
83 const checkForUpdates = () => {
84 if (didUnsubscribe)
85 return;
86 const value = subscription.getCurrentValue();
87 setState((prevState) => {
88 if (prevState.value === value) {
89 return prevState;
90 }
91 return { value };
92 });
93 };
94 let unsubscribe = subscription.subscribe(checkForUpdates);
95 checkForUpdates();
96 return () => {
97 didUnsubscribe = true;
98 unsubscribe();
99 };
100 }, [subscription]);
101 return valueToReturn;
102};