Files
sentinel/static/app/utils/useSyncedLocalStorageState.tsx
2024-02-15 17:01:50 +00:00

63 lines
1.7 KiB
TypeScript

import {useCallback, useEffect} from 'react';
import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
type SyncedLocalStorageEvent<S> = CustomEvent<{key: string; value: S}>;
const SYNCED_STORAGE_EVENT = 'synced-local-storage';
function isCustomEvent(event: Event): event is CustomEvent {
return 'detail' in event;
}
function isSyncedLocalStorageEvent<S>(
event: Event,
key: string
): event is SyncedLocalStorageEvent<S> {
return (
isCustomEvent(event) &&
event.type === SYNCED_STORAGE_EVENT &&
event.detail.key === key
);
}
/**
* Same as `useLocalStorageState`, but notifies and reacts to state changes.
* Use this you want to access local storage state from multiple components
* on the same page.
*/
export function useSyncedLocalStorageState<S>(
key: string,
initialState: S | ((value?: unknown, rawValue?: unknown) => S)
): [S, (value: S) => void] {
const [value, setValue] = useLocalStorageState(key, initialState);
const setValueAndNotify = useCallback(
newValue => {
setValue(newValue);
// We use a custom event to notify all consumers of this hook
window.dispatchEvent(
new CustomEvent(SYNCED_STORAGE_EVENT, {detail: {key, value: newValue}})
);
},
[key, setValue]
);
useEffect(() => {
const handleNewSyncedLocalStorageEvent = (event: Event) => {
if (isSyncedLocalStorageEvent<S>(event, key)) {
setValue(event.detail.value);
}
};
window.addEventListener(SYNCED_STORAGE_EVENT, handleNewSyncedLocalStorageEvent);
return () => {
window.removeEventListener(SYNCED_STORAGE_EVENT, handleNewSyncedLocalStorageEvent);
};
}, [key, setValue, value]);
return [value, setValueAndNotify];
}