Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 62b6f6e

Browse files
committed
chore: flesh out more of React class
1 parent 60b2220 commit 62b6f6e

File tree

2 files changed

+50
-24
lines changed

2 files changed

+50
-24
lines changed

site/src/hooks/useTimeSync.tsx

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,45 +39,74 @@ type ReactTimeSyncSubscriptionEntry = Readonly<
3939
}
4040
>;
4141

42+
// Need to wrap each value that we put in the selection cache, so that when we
43+
// try to retrieve a value, it's easy to differentiate between a value being
44+
// undefined because that's an explicit selection value, versus it being
45+
// undefined because we forgot to set it in the cache
46+
type SelectionCacheEntry = Readonly<{ value: unknown }>;
47+
4248
interface ReactTimeSyncApi {
4349
subscribe: (entry: ReactTimeSyncSubscriptionEntry) => () => void;
4450
getSelectionSnapshot: <T = unknown>(id: string) => T;
4551
}
4652

4753
class ReactTimeSync implements ReactTimeSyncApi {
4854
readonly #timeSync: TimeSync;
49-
readonly #resyncOnNewSubscription: boolean;
50-
readonly #selectionCache: Map<string, unknown>;
55+
readonly #selectionCache: Map<string, SelectionCacheEntry>;
5156

5257
constructor(options: Partial<TimeSyncInitOptions>) {
53-
const {
54-
resyncOnNewSubscription = defaultOptions.resyncOnNewSubscription,
55-
initialDatetime = defaultOptions.initialDatetime,
56-
createNewDatetime = defaultOptions.createNewDatetime,
57-
setInterval = defaultOptions.setInterval,
58-
clearInterval = defaultOptions.clearInterval,
59-
} = options;
60-
58+
this.#timeSync = new TimeSync(options);
6159
this.#selectionCache = new Map();
62-
this.#resyncOnNewSubscription = resyncOnNewSubscription;
63-
this.#timeSync = new TimeSync({
64-
initialDatetime,
65-
createNewDatetime,
66-
setInterval,
67-
clearInterval,
68-
});
6960
}
7061

7162
// All functions that are part of the public interface must be defined as
7263
// arrow functions, so that they work properly with React
7364

7465
subscribe = (entry: ReactTimeSyncSubscriptionEntry): (() => void) => {
75-
this.#timeSync.subscribe(entry);
76-
return () => this.#timeSync.unsubscribe(entry.id);
66+
const { select, id, idealRefreshIntervalMs, onUpdate } = entry;
67+
68+
// Make sure that we subscribe first, in case TimeSync is configured to
69+
// invalidate the snapshot on a new subscription. Want to remove risk of
70+
// stale data
71+
const patchedEntry: SubscriptionEntry = {
72+
id,
73+
idealRefreshIntervalMs,
74+
onUpdate: (newDate) => {
75+
const prevSelection = this.getSelectionSnapshot(id);
76+
const newSelection: unknown = select?.(newDate) ?? newDate;
77+
if (newSelection === prevSelection) {
78+
return;
79+
}
80+
81+
this.#selectionCache.set(id, { value: newSelection });
82+
onUpdate(newDate);
83+
},
84+
};
85+
this.#timeSync.subscribe(patchedEntry);
86+
87+
const date = this.#timeSync.getTimeSnapshot();
88+
const cacheValue = select?.(date) ?? date;
89+
this.#selectionCache.set(id, { value: cacheValue });
90+
91+
return () => this.#timeSync.unsubscribe(id);
7792
};
7893

94+
/**
95+
* Allows you to grab the result of a selection that has been registered
96+
* with ReactTimeSync.
97+
*
98+
* If this method is called with an ID before a subscription has been
99+
* registered for that ID, that will cause the method to throw.
100+
*/
79101
getSelectionSnapshot = <T,>(id: string): T => {
80-
return this.#selectionCache.get(id) as T;
102+
const cacheEntry = this.#selectionCache.get(id);
103+
if (cacheEntry === undefined) {
104+
throw new Error(
105+
"Trying to retrieve value from selection cache without it being initialized",
106+
);
107+
}
108+
109+
return cacheEntry.value as T;
81110
};
82111
}
83112

@@ -177,7 +206,7 @@ export function useTimeSync<T = Date>(options: UseTimeSyncOptions<T>): T {
177206
});
178207

179208
// We need to define this callback using useCallback instead of
180-
// useEffectEvent because we want the memoization to be invaliated when the
209+
// useEffectEvent because we want the memoization to be invalidated when the
181210
// refresh interval changes. (When the subscription callback changes by
182211
// reference, that causes useSyncExternalStore to redo the subscription with
183212
// the new callback). All other values need to be included in the dependency

site/src/utils/TimeSync.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ interface TimeSyncApi {
6262
subscribe: (entry: SubscriptionEntry) => void;
6363
unsubscribe: (id: string) => void;
6464
getTimeSnapshot: () => Date;
65-
invalidateSnapshot: () => void;
6665
}
6766

6867
/**
@@ -167,8 +166,6 @@ export class TimeSync implements TimeSyncApi {
167166
return this.#latestDateSnapshot;
168167
}
169168

170-
invalidateSnapshot(): void {}
171-
172169
unsubscribe(id: string): void {
173170
const updated = this.#subscriptions.filter((s) => s.id !== id);
174171
if (updated.length === this.#subscriptions.length) {

0 commit comments

Comments
 (0)