diff options
author | dan <dan.abramov@gmail.com> | 2024-08-06 01:30:52 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-06 01:30:52 +0100 |
commit | 686d5ebb535710dd8c96aa694b4cd1f7913ff3fa (patch) | |
tree | 35ca7e7646d455468c97520b8cb972e8555fc3f2 /src/state/persisted/index.web.ts | |
parent | 966f6c511fff510fc011aa5c426c6b7eaf4f21ac (diff) | |
download | voidsky-686d5ebb535710dd8c96aa694b4cd1f7913ff3fa.tar.zst |
[Persisted] Make broadcast subscriptions granular by key (#4874)
* Add fast path for guaranteed noop updates * Change persisted.onUpdate() API to take a key * Implement granular broadcast listeners
Diffstat (limited to 'src/state/persisted/index.web.ts')
-rw-r--r-- | src/state/persisted/index.web.ts | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/src/state/persisted/index.web.ts b/src/state/persisted/index.web.ts index d71b59096..7521776bc 100644 --- a/src/state/persisted/index.web.ts +++ b/src/state/persisted/index.web.ts @@ -47,18 +47,36 @@ export async function write<K extends keyof Schema>( // Don't fire the update listeners yet to avoid a loop. // If there was a change, we'll receive the broadcast event soon enough which will do that. } + try { + if (JSON.stringify({v: _state[key]}) === JSON.stringify({v: value})) { + // Fast path for updates that are guaranteed to be noops. + // This is good mostly because it avoids useless broadcasts to other tabs. + return + } + } catch (e) { + // Ignore and go through the normal path. + } _state = { ..._state, [key]: value, } writeToStorage(_state) - broadcast.postMessage({event: UPDATE_EVENT}) + broadcast.postMessage({event: {type: UPDATE_EVENT, key}}) + broadcast.postMessage({event: UPDATE_EVENT}) // Backcompat while upgrading } write satisfies PersistedApi['write'] -export function onUpdate(cb: () => void): () => void { - _emitter.addListener('update', cb) - return () => _emitter.removeListener('update', cb) +export function onUpdate<K extends keyof Schema>( + key: K, + cb: (v: Schema[K]) => void, +): () => void { + const listener = () => cb(get(key)) + _emitter.addListener('update', listener) // Backcompat while upgrading + _emitter.addListener('update:' + key, listener) + return () => { + _emitter.removeListener('update', listener) // Backcompat while upgrading + _emitter.removeListener('update:' + key, listener) + } } onUpdate satisfies PersistedApi['onUpdate'] @@ -72,12 +90,23 @@ export async function clearStorage() { clearStorage satisfies PersistedApi['clearStorage'] async function onBroadcastMessage({data}: MessageEvent) { - if (typeof data === 'object' && data.event === UPDATE_EVENT) { + if ( + typeof data === 'object' && + (data.event === UPDATE_EVENT || // Backcompat while upgrading + data.event?.type === UPDATE_EVENT) + ) { // read next state, possibly updated by another tab const next = readFromStorage() + if (next === _state) { + return + } if (next) { _state = next - _emitter.emit('update') + if (typeof data.event.key === 'string') { + _emitter.emit('update:' + data.event.key) + } else { + _emitter.emit('update') // Backcompat while upgrading + } } else { logger.error( `persisted state: handled update update from broadcast channel, but found no data`, |